package test import ( "slices" "testing" "fiatjaf.com/nostr" "fiatjaf.com/nostr/eventstore" "github.com/stretchr/testify/require" ) func basicTest(t *testing.T, db eventstore.Store) { err := db.Init() require.NoError(t, err) // define public keys for use throughout the test pk3 := nostr.GetPublicKey(sk3) pk4 := nostr.GetPublicKey(sk4) // from basic-test.patch { // create test events with different tags and authors events := []nostr.Event{ // event with 'e' tag { CreatedAt: 100, Content: "event with e tag", Tags: nostr.Tags{{"e", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}, Kind: 1, }, // event with 'q' tag { CreatedAt: 101, Content: "event with q tag", Tags: nostr.Tags{{"q", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}, Kind: 1, }, // event with 'p' tag and kind 3 { CreatedAt: 102, Content: "event with p tag kind 3", Tags: nostr.Tags{{"p", pk3.Hex()}}, Kind: 3, }, // event with 'p' tag and kind 7 { CreatedAt: 103, Content: "event with p tag kind 7", Tags: nostr.Tags{{"p", pk4.Hex()}}, Kind: 7, }, // event from author pk3 with kind 1 { CreatedAt: 104, Content: "event from pk3 kind 1", Tags: nostr.Tags{}, Kind: 1, }, // event from author pk4 with kind 3 { CreatedAt: 105, Content: "event from pk4 kind 3", Tags: nostr.Tags{}, Kind: 3, }, } // sign events with appropriate keys events[0].Sign(sk3) events[1].Sign(sk3) events[2].Sign(sk3) events[3].Sign(sk3) events[4].Sign(sk3) events[5].Sign(sk4) // save all events for _, evt := range events { err = db.SaveEvent(evt) require.NoError(t, err) } // test 0: query all { results := slices.Collect(db.QueryEvents(nostr.Filter{}, 1000)) require.NoError(t, err) require.Len(t, results, 6) } // test 1: query by 'e' tag { results := slices.Collect(db.QueryEvents(nostr.Filter{ Tags: nostr.TagMap{"e": []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}, }, 1000)) require.NoError(t, err) require.Len(t, results, 1) require.Equal(t, events[0].ID, results[0].ID, "e tag query error") } // test 2: query by 'q' tag { results := slices.Collect(db.QueryEvents(nostr.Filter{ Tags: nostr.TagMap{"q": []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}, }, 1000)) require.NoError(t, err) require.Len(t, results, 1) require.Equal(t, events[1].ID, results[0].ID, "q tag query error") } // test 3: query by 'p' tag + kind { results := slices.Collect(db.QueryEvents(nostr.Filter{ Tags: nostr.TagMap{"p": []string{pk3.Hex()}}, Kinds: []nostr.Kind{3}, }, 1000)) require.NoError(t, err) require.Len(t, results, 1) require.Equal(t, events[2].ID, results[0].ID, "p tag + kind query error") } // test 4: query by author + kind { results := slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk4}, Kinds: []nostr.Kind{3}, }, 1000)) require.NoError(t, err) require.Len(t, results, 1) require.Equal(t, events[5].ID, results[0].ID, "author + kind query error") } } // from another-basic-test.patch { evt1 := nostr.Event{ CreatedAt: nostr.Now(), Content: "two tags", Tags: nostr.Tags{ {"e", "f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}, {"q", "f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}, }, Kind: 23122, } evt1.Sign(sk3) require.NoError(t, db.SaveEvent(evt1)) { results := slices.Collect(db.QueryEvents(nostr.Filter{ Tags: nostr.TagMap{"e": []string{"f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}}, }, 1000)) require.NoError(t, err) require.Len(t, results, 1) require.ElementsMatch(t, []nostr.Event{evt1}, results, "querying by 'e' tag") } { results := slices.Collect(db.QueryEvents(nostr.Filter{ Tags: nostr.TagMap{"q": []string{"f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}}, }, 1000)) require.NoError(t, err) require.Len(t, results, 1) require.ElementsMatch(t, []nostr.Event{evt1}, results, "querying by 'q' tag") } evt2 := nostr.Event{ CreatedAt: nostr.Now(), Content: "e tag", Tags: nostr.Tags{ {"e", "f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}, }, Kind: 23122, } evt2.Sign(sk3) require.NoError(t, db.SaveEvent(evt2)) evt3 := nostr.Event{ CreatedAt: nostr.Now(), Content: "q tag", Tags: nostr.Tags{ {"q", "f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}, }, Kind: 23122, } evt3.Sign(sk3) require.NoError(t, db.SaveEvent(evt3)) { results := slices.Collect(db.QueryEvents(nostr.Filter{ Tags: nostr.TagMap{"e": []string{"f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}}, }, 1000)) require.NoError(t, err) require.Len(t, results, 2) require.ElementsMatch(t, []nostr.Event{evt1, evt2}, results, "querying by 'e' tag") } { results := slices.Collect(db.QueryEvents(nostr.Filter{ Tags: nostr.TagMap{"q": []string{"f355341a03672c5b136a6002fb4b69ad52111a1646638771c3995fc0a4db2a78"}}, }, 1000)) require.NoError(t, err) require.Len(t, results, 2) require.ElementsMatch(t, []nostr.Event{evt1, evt3}, results, "querying by 'q' tag") } } // test ReplaceEvent() { originalProfile := nostr.Event{ CreatedAt: 200, Content: `{"name":"original","about":"original profile"}`, Tags: nostr.Tags{}, Kind: 0, } originalProfile.Sign(sk3) err = db.ReplaceEvent(originalProfile) require.NoError(t, err) // verify results := slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{0}, }, 1000)) require.Len(t, results, 1) require.Equal(t, originalProfile.ID, results[0].ID) // create newer profile event newProfile := nostr.Event{ CreatedAt: 300, // newer timestamp Content: `{"name":"updated","about":"updated profile"}`, Tags: nostr.Tags{}, Kind: 0, } newProfile.Sign(sk3) // replace with newer event err = db.ReplaceEvent(newProfile) require.NoError(t, err) // verify only the newer event exists results = slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{0}, }, 1000)) require.Len(t, results, 1) require.Equal(t, newProfile.ID, results[0].ID) // try to replace with older event (should be ignored) olderProfile := nostr.Event{ CreatedAt: 250, // older than current Content: `{"name":"older","about":"older profile"}`, Tags: nostr.Tags{}, Kind: 0, } olderProfile.Sign(sk3) err = db.ReplaceEvent(olderProfile) require.NoError(t, err) // verify the newer event is still there results = slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{0}, }, 1000)) require.Len(t, results, 1) require.Equal(t, newProfile.ID, results[0].ID) // test addressable event (kind 30023 - article) articleV1 := nostr.Event{ CreatedAt: 400, Content: "first version of article", Tags: nostr.Tags{{"d", "my-article"}}, // addressable identifier Kind: 30023, // article - addressable } articleV1.Sign(sk3) err = db.ReplaceEvent(articleV1) require.NoError(t, err) // verify article was saved results = slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{30023}, Tags: nostr.TagMap{"d": []string{"my-article"}}, }, 1000)) require.Len(t, results, 1) require.Equal(t, articleV1.ID, results[0].ID) // create updated version of same article articleV2 := nostr.Event{ CreatedAt: 500, Content: "second version of article", Tags: nostr.Tags{{"d", "my-article"}}, // same identifier Kind: 30023, } articleV2.Sign(sk3) err = db.ReplaceEvent(articleV2) require.NoError(t, err) // verify only the newer version exists results = slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{30023}, Tags: nostr.TagMap{"d": []string{"my-article"}}, }, 1000)) require.Len(t, results, 1) require.Equal(t, articleV2.ID, results[0].ID) require.Equal(t, "second version of article", results[0].Content) // create different article with different d tag differentArticle := nostr.Event{ CreatedAt: 600, Content: "different article", Tags: nostr.Tags{{"d", "other-article"}}, // different identifier Kind: 30023, } differentArticle.Sign(sk3) err = db.ReplaceEvent(differentArticle) require.NoError(t, err) // verify both articles exist (different d tags) results = slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{30023}, }, 1000)) require.Len(t, results, 2) } // test DeleteEvent() { // create other events to ensure they are not deleted otherEvent1 := nostr.Event{ CreatedAt: 601, Content: "other event 1", Tags: nostr.Tags{}, Kind: 1, } otherEvent1.Sign(sk3) err = db.SaveEvent(otherEvent1) require.NoError(t, err) otherEvent2 := nostr.Event{ CreatedAt: 602, Content: "other event 2", Tags: nostr.Tags{}, Kind: 2, } otherEvent2.Sign(sk4) err = db.SaveEvent(otherEvent2) require.NoError(t, err) // create a test event to delete deleteEvent := nostr.Event{ CreatedAt: 600, Content: "event to be deleted", Tags: nostr.Tags{}, Kind: 1, } deleteEvent.Sign(sk3) err = db.SaveEvent(deleteEvent) require.NoError(t, err) // verify events exist results := slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{1}, }, 1000)) require.Contains(t, results, deleteEvent) require.Contains(t, results, otherEvent1) results2 := slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk4}, Kinds: []nostr.Kind{2}, }, 1000)) require.Contains(t, results2, otherEvent2) // delete the event err = db.DeleteEvent(deleteEvent.ID) require.NoError(t, err) // verify event is deleted results = slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk3}, Kinds: []nostr.Kind{1}, }, 1000)) require.NotContains(t, results, deleteEvent) require.Contains(t, results, otherEvent1) // verify other event still exists results2 = slices.Collect(db.QueryEvents(nostr.Filter{ Authors: []nostr.PubKey{pk4}, Kinds: []nostr.Kind{2}, }, 1000)) require.Contains(t, results2, otherEvent2) // test deleting non-existent event (should not error) fakeID, _ := nostr.IDFromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") err = db.DeleteEvent(fakeID) require.NoError(t, err) } }