refactor(postgres): Unit test SaveEvent

Refactors SaveEvent so it's unit testable and adds tests to
assert the current behavior.
This commit is contained in:
bndw 2023-05-03 17:18:47 -07:00 committed by fiatjaf_
parent f328910ab6
commit a7dbf7c491
2 changed files with 226 additions and 22 deletions
storage/postgresql

@ -9,30 +9,13 @@ import (
)
func (b *PostgresBackend) SaveEvent(ctx context.Context, evt *nostr.Event) error {
// react to different kinds of events
if evt.Kind == nostr.KindSetMetadata || evt.Kind == nostr.KindContactList || (10000 <= evt.Kind && evt.Kind < 20000) {
// delete past events from this user
b.DB.ExecContext(ctx, `DELETE FROM event WHERE pubkey = $1 AND kind = $2`, evt.PubKey, evt.Kind)
} else if evt.Kind == nostr.KindRecommendServer {
// delete past recommend_server events equal to this one
b.DB.ExecContext(ctx, `DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND content = $3`,
evt.PubKey, evt.Kind, evt.Content)
} else if evt.Kind >= 30000 && evt.Kind < 40000 {
// NIP-33
d := evt.Tags.GetFirst([]string{"d"})
if d != nil {
b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND tagvalues && ARRAY[$3]`,
evt.PubKey, evt.Kind, d.Value())
}
deleteQuery, deleteParams, shouldDelete := deleteBeforeSaveSql(evt)
if shouldDelete {
_, _ = b.DB.ExecContext(ctx, deleteQuery, deleteParams...)
}
// insert
tagsj, _ := json.Marshal(evt.Tags)
res, err := b.DB.ExecContext(ctx, `
INSERT INTO event (id, pubkey, created_at, kind, tags, content, sig)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (id) DO NOTHING
`, evt.ID, evt.PubKey, evt.CreatedAt, evt.Kind, tagsj, evt.Content, evt.Sig)
sql, params, _ := saveEventSql(evt)
res, err := b.DB.ExecContext(ctx, sql, params...)
if err != nil {
return err
}
@ -60,3 +43,47 @@ func (b *PostgresBackend) AfterSave(evt *nostr.Event) {
ORDER BY created_at DESC OFFSET 100 LIMIT 1
)`, evt.PubKey, evt.Kind)
}
func deleteBeforeSaveSql(evt *nostr.Event) (string, []any, bool) {
// react to different kinds of events
var (
query = ""
params []any
shouldDelete bool
)
if evt.Kind == nostr.KindSetMetadata || evt.Kind == nostr.KindContactList || (10000 <= evt.Kind && evt.Kind < 20000) {
// delete past events from this user
query = `DELETE FROM event WHERE pubkey = $1 AND kind = $2`
params = []any{evt.PubKey, evt.Kind}
shouldDelete = true
} else if evt.Kind == nostr.KindRecommendServer {
// delete past recommend_server events equal to this one
query = `DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND content = $3`
params = []any{evt.PubKey, evt.Kind, evt.Content}
shouldDelete = true
} else if evt.Kind >= 30000 && evt.Kind < 40000 {
// NIP-33
d := evt.Tags.GetFirst([]string{"d"})
if d != nil {
query = `DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND tagvalues && ARRAY[$3]`
params = []any{evt.PubKey, evt.Kind, d.Value()}
shouldDelete = true
}
}
return query, params, shouldDelete
}
func saveEventSql(evt *nostr.Event) (string, []any, error) {
const query = `INSERT INTO event (
id, pubkey, created_at, kind, tags, content, sig)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (id) DO NOTHING`
var (
tagsj, _ = json.Marshal(evt.Tags)
params = []any{evt.ID, evt.PubKey, evt.CreatedAt, evt.Kind, tagsj, evt.Content, evt.Sig}
)
return query, params, nil
}

@ -0,0 +1,177 @@
package postgresql
import (
"testing"
"github.com/nbd-wtf/go-nostr"
"github.com/stretchr/testify/assert"
)
func TestDeleteBeforeSave(t *testing.T) {
var tests = []struct {
name string
event *nostr.Event
query string
params []any
shouldDelete bool
}{
{
name: "set metadata",
event: &nostr.Event{
Kind: nostr.KindSetMetadata,
PubKey: "pk",
},
query: "DELETE FROM event WHERE pubkey = $1 AND kind = $2",
params: []any{"pk", nostr.KindSetMetadata},
shouldDelete: true,
},
{
name: "contact list",
event: &nostr.Event{
Kind: nostr.KindContactList,
PubKey: "pk",
},
query: "DELETE FROM event WHERE pubkey = $1 AND kind = $2",
params: []any{"pk", nostr.KindContactList},
shouldDelete: true,
},
{
name: "recommend server",
event: &nostr.Event{
Kind: nostr.KindRecommendServer,
PubKey: "pk",
Content: "test",
},
query: "DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND content = $3",
params: []any{"pk", nostr.KindRecommendServer, "test"},
shouldDelete: true,
},
{
name: "nip-33",
event: &nostr.Event{
Kind: 31000,
PubKey: "pk",
Tags: nostr.Tags{nostr.Tag{"d", "value"}},
},
query: "DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND tagvalues && ARRAY[$3]",
params: []any{"pk", 31000, "value"},
shouldDelete: true,
},
{
name: "kind > 10000",
event: &nostr.Event{
Kind: 10001,
PubKey: "pk",
},
query: "DELETE FROM event WHERE pubkey = $1 AND kind = $2",
params: []any{"pk", 10001},
shouldDelete: true,
},
{
name: "kind < 20000",
event: &nostr.Event{
Kind: 19999,
PubKey: "pk",
},
query: "DELETE FROM event WHERE pubkey = $1 AND kind = $2",
params: []any{"pk", 19999},
shouldDelete: true,
},
// Should not delete cases
{
name: "kind < 10000",
event: &nostr.Event{
Kind: 9999,
PubKey: "pk",
},
query: "",
params: nil,
shouldDelete: false,
},
{
name: "kind > 21000",
event: &nostr.Event{
Kind: 21000,
PubKey: "pk",
},
query: "",
params: nil,
shouldDelete: false,
},
{
name: "kind 1",
event: &nostr.Event{
Kind: nostr.KindTextNote,
PubKey: "pk",
},
query: "",
params: nil,
shouldDelete: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
query, params, shouldDelete := deleteBeforeSaveSql(tt.event)
assert.Equal(t, tt.query, query)
assert.Equal(t, tt.params, params)
assert.Equal(t, tt.shouldDelete, shouldDelete)
})
}
}
func TestSaveEventSql(t *testing.T) {
now := nostr.Now()
var tests = []struct {
name string
event *nostr.Event
query string
params []any
err error
}{
{
name: "basic",
event: &nostr.Event{
ID: "id",
PubKey: "pk",
CreatedAt: now,
Kind: nostr.KindTextNote,
Content: "test",
Sig: "sig",
},
query: `INSERT INTO event (
id, pubkey, created_at, kind, tags, content, sig)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (id) DO NOTHING`,
params: []any{"id", "pk", now, nostr.KindTextNote, []byte("null"), "test", "sig"},
err: nil,
},
{
name: "tags",
event: &nostr.Event{
ID: "id",
PubKey: "pk",
CreatedAt: now,
Kind: nostr.KindTextNote,
Tags: nostr.Tags{nostr.Tag{"foo", "bar"}},
Content: "test",
Sig: "sig",
},
query: `INSERT INTO event (
id, pubkey, created_at, kind, tags, content, sig)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (id) DO NOTHING`,
params: []any{"id", "pk", now, nostr.KindTextNote, []byte("[[\"foo\",\"bar\"]]"), "test", "sig"},
err: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
query, params, err := saveEventSql(tt.event)
assert.Equal(t, clean(tt.query), clean(query))
assert.Equal(t, tt.params, params)
assert.Equal(t, tt.err, err)
})
}
}