Files
next.orly.dev/pkg/database/query-events-multiple-param-replaceable_test.go
mleku 24383ef1f4
Some checks failed
Go / build-and-release (push) Has been cancelled
Decompose handle-event.go into DDD domain services (v0.36.15)
Major refactoring of event handling into clean, testable domain services:

- Add pkg/event/validation: JSON hex validation, signature verification,
  timestamp bounds, NIP-70 protected tag validation
- Add pkg/event/authorization: Policy and ACL authorization decisions,
  auth challenge handling, access level determination
- Add pkg/event/routing: Event router registry with ephemeral and delete
  handlers, kind-based dispatch
- Add pkg/event/processing: Event persistence, delivery to subscribers,
  and post-save hooks (ACL reconfig, sync, relay groups)
- Reduce handle-event.go from 783 to 296 lines (62% reduction)
- Add comprehensive unit tests for all new domain services
- Refactor database tests to use shared TestMain setup
- Fix blossom URL test expectations (missing "/" separator)
- Add go-memory-optimization skill and analysis documentation
- Update DDD_ANALYSIS.md to reflect completed decomposition

Files modified:
- app/handle-event.go: Slim orchestrator using domain services
- app/server.go: Service initialization and interface wrappers
- app/handle-event-types.go: Shared types (OkHelper, result types)
- pkg/event/validation/*: New validation service package
- pkg/event/authorization/*: New authorization service package
- pkg/event/routing/*: New routing service package
- pkg/event/processing/*: New processing service package
- pkg/database/*_test.go: Refactored to shared TestMain
- pkg/blossom/http_test.go: Fixed URL format expectations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 05:30:07 +01:00

160 lines
4.7 KiB
Go

package database
import (
"fmt"
"testing"
"git.mleku.dev/mleku/nostr/encoders/event"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/hex"
"git.mleku.dev/mleku/nostr/encoders/kind"
"git.mleku.dev/mleku/nostr/encoders/tag"
"git.mleku.dev/mleku/nostr/encoders/timestamp"
"git.mleku.dev/mleku/nostr/interfaces/signer/p8k"
"lol.mleku.dev/chk"
"next.orly.dev/pkg/utils"
)
// TestMultipleParameterizedReplaceableEvents tests that when multiple parameterized
// replaceable events with the same pubkey, kind, and d-tag exist, only the newest one
// is returned in query results.
func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
// Needs fresh database (modifies data)
db, ctx, cleanup := setupFreshTestDB(t)
defer cleanup()
sign := p8k.MustNew()
if err := sign.Generate(); chk.E(err) {
t.Fatal(err)
}
// Create a base parameterized replaceable event
baseEvent := event.New()
baseEvent.Kind = kind.ParameterizedReplaceableStart.K // Kind 30000+ is parameterized replaceable
baseEvent.CreatedAt = timestamp.Now().V - 7200 // 2 hours ago
baseEvent.Content = []byte("Original parameterized event")
baseEvent.Tags = tag.NewS()
// Add a d-tag
*baseEvent.Tags = append(
*baseEvent.Tags, tag.NewFromAny("d", "test-d-tag"),
)
baseEvent.Sign(sign)
// Save the base parameterized replaceable event
if _, err := db.SaveEvent(ctx, baseEvent); err != nil {
t.Fatalf("Failed to save base parameterized replaceable event: %v", err)
}
// Create a newer parameterized replaceable event with the same pubkey, kind, and d-tag
newerEvent := event.New()
newerEvent.Kind = baseEvent.Kind // Same parameterized kind
newerEvent.CreatedAt = timestamp.Now().V - 3600 // 1 hour ago (newer than base event)
newerEvent.Content = []byte("Newer parameterized event")
newerEvent.Tags = tag.NewS()
// Add the same d-tag
*newerEvent.Tags = append(
*newerEvent.Tags,
tag.NewFromAny("d", "test-d-tag"),
)
newerEvent.Sign(sign)
// Save the newer parameterized replaceable event
if _, err := db.SaveEvent(ctx, newerEvent); err != nil {
t.Fatalf(
"Failed to save newer parameterized replaceable event: %v", err,
)
}
// Create an even newer parameterized replaceable event with the same pubkey, kind, and d-tag
newestEvent := event.New()
newestEvent.Kind = baseEvent.Kind // Same parameterized kind
newestEvent.CreatedAt = timestamp.Now().V // Current time (newest)
newestEvent.Content = []byte("Newest parameterized event")
newestEvent.Tags = tag.NewS()
// Add the same d-tag
*newestEvent.Tags = append(
*newestEvent.Tags,
tag.NewFromAny("d", "test-d-tag"),
)
newestEvent.Sign(sign)
// Save the newest parameterized replaceable event
if _, err := db.SaveEvent(ctx, newestEvent); err != nil {
t.Fatalf(
"Failed to save newest parameterized replaceable event: %v", err,
)
}
// Query for all events of this kind and pubkey
paramKindFilter := kind.NewS(kind.New(baseEvent.Kind))
paramAuthorFilter := tag.NewFromBytesSlice(baseEvent.Pubkey)
evs, err := db.QueryEvents(
ctx, &filter.F{
Kinds: paramKindFilter,
Authors: paramAuthorFilter,
},
)
if err != nil {
t.Fatalf(
"Failed to query for parameterized replaceable events: %v", err,
)
}
// Print debug info about the returned events
fmt.Printf("Debug: Got %d events\n", len(evs))
for i, ev := range evs {
fmt.Printf(
"Debug: Event %d: kind=%d, pubkey=%s, created_at=%d, content=%s\n",
i, ev.Kind, hex.Enc(ev.Pubkey), ev.CreatedAt, ev.Content,
)
dTag := ev.Tags.GetFirst([]byte("d"))
if dTag != nil && dTag.Len() > 1 {
fmt.Printf("Debug: Event %d: d-tag=%s\n", i, dTag.Value())
}
}
// Verify we get exactly one event (the newest one)
if len(evs) != 1 {
t.Fatalf(
"Expected 1 event when querying for parameterized replaceable events, got %d",
len(evs),
)
}
// Verify it's the newest event
if !utils.FastEqual(evs[0].ID, newestEvent.ID) {
t.Fatalf(
"Event ID doesn't match the newest event. Got %x, expected %x",
evs[0].ID, newestEvent.ID,
)
}
// Verify the content is from the newest event
if string(evs[0].Content) != string(newestEvent.Content) {
t.Fatalf(
"Event content doesn't match the newest event. Got %s, expected %s",
evs[0].Content, newestEvent.Content,
)
}
// Query for the base event by ID
evs, err = db.QueryEvents(
ctx, &filter.F{
Ids: tag.NewFromBytesSlice(baseEvent.ID),
},
)
if err != nil {
t.Fatalf("Failed to query for base event by ID: %v", err)
}
// Verify we get 1 event when querying for the base event by ID
// Replaced events should still be accessible by their ID
if len(evs) != 1 {
t.Fatalf(
"Expected 1 event when querying for replaced base event by ID, got %d",
len(evs),
)
}
}