mirror of
https://github.com/Cameri/nostream.git
synced 2025-03-17 21:31:48 +01:00
feat: upsert kind 41 events
This commit is contained in:
parent
afbda707f8
commit
41ae842d7a
@ -216,7 +216,7 @@ export class EventRepository implements IEventRepository {
|
||||
// NIP-33: Parameterized Replaceable Events
|
||||
.onConflict(
|
||||
this.dbClient.raw(
|
||||
'(event_pubkey, event_kind, event_deduplication) WHERE (event_kind = 0 OR event_kind = 3 OR (event_kind >= 10000 AND event_kind < 20000)) OR (event_kind >= 30000 AND event_kind < 40000)'
|
||||
'(event_pubkey, event_kind, event_deduplication) WHERE (event_kind = 0 OR event_kind = 3 OR event_kind = 41 OR (event_kind >= 10000 AND event_kind < 20000)) OR (event_kind >= 30000 AND event_kind < 40000)'
|
||||
)
|
||||
)
|
||||
.merge(omit(['event_pubkey', 'event_kind', 'event_deduplication'])(row))
|
||||
|
@ -162,6 +162,7 @@ export const isEventSignatureValid = async (event: Event): Promise<boolean> => {
|
||||
export const isReplaceableEvent = (event: Event): boolean => {
|
||||
return event.kind === EventKinds.SET_METADATA
|
||||
|| event.kind === EventKinds.CONTACT_LIST
|
||||
|| event.kind === EventKinds.CHANNEL_METADATA
|
||||
|| (event.kind >= EventKinds.REPLACEABLE_FIRST && event.kind <= EventKinds.REPLACEABLE_LAST)
|
||||
}
|
||||
|
||||
|
38
test/integration/features/nip-28/nip-28.feature
Normal file
38
test/integration/features/nip-28/nip-28.feature
Normal file
@ -0,0 +1,38 @@
|
||||
Feature: NIP-28
|
||||
Scenario: Alice creates a channel
|
||||
Given someone called Alice
|
||||
When Alice sends a channel_creation event with content '{\"name\": \"Demo Channel\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
And Alice subscribes to last event from Alice
|
||||
Then Alice receives a channel_creation event from Alice with content '{\"name\": \"Demo Channel\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
|
||||
Scenario: Alice sets metadata for a channel
|
||||
Given someone called Alice
|
||||
And Alice subscribes to author Alice
|
||||
And Alice sends a channel_creation event with content '{\"name\": \"Original\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
And Alice receives a channel_creation event from Alice with content '{\"name\": \"Original\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
When Alice sends a channel_metadata event with content '{\"name\": \"New\", \"about\": \"A better test channel.\", \"picture\": \"https://placekitten.com/256/256\"}'
|
||||
Then Alice receives a channel_metadata event from Alice with content '{\"name\": \"New\", \"about\": \"A better test channel.\", \"picture\": \"https://placekitten.com/256/256\"}'
|
||||
|
||||
Scenario: Alice replaces metadata for a channel
|
||||
Given someone called Alice
|
||||
And Alice subscribes to author Alice
|
||||
And Alice sends a channel_creation event with content '{\"name\": \"Original\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
And Alice receives a channel_creation event from Alice with content '{\"name\": \"Original\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
And Alice sends a channel_metadata event with content '{\"name\": \"New\", \"about\": \"A better test channel.\", \"picture\": \"https://placekitten.com/256/256\"}'
|
||||
And Alice receives a channel_metadata event from Alice with content '{\"name\": \"New\", \"about\": \"A better test channel.\", \"picture\": \"https://placekitten.com/256/256\"}'
|
||||
When Alice sends a channel_metadata event with content '{\"name\": \"Replaced\", \"about\": \"A different test channel.\", \"picture\": \"https://placekitten.com/400/400\"}'
|
||||
Then Alice receives a channel_metadata event from Alice with content '{\"name\": \"Replaced\", \"about\": \"A different test channel.\", \"picture\": \"https://placekitten.com/400/400\"}'
|
||||
|
||||
Scenario: Alice replaces metadata for a channel
|
||||
Given someone called Alice
|
||||
And Alice subscribes to author Alice
|
||||
And Alice sends a channel_creation event with content '{\"name\": \"Original\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
And Alice receives a channel_creation event from Alice with content '{\"name\": \"Original\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
And Alice sends a channel_metadata event with content '{\"name\": \"New\", \"about\": \"A better test channel.\", \"picture\": \"https://placekitten.com/256/256\"}'
|
||||
And Alice receives a channel_metadata event from Alice with content '{\"name\": \"New\", \"about\": \"A better test channel.\", \"picture\": \"https://placekitten.com/256/256\"}'
|
||||
And Alice unsubscribes from author Alice
|
||||
When Alice sends a channel_metadata event with content '{\"name\": \"Replaced\", \"about\": \"A different test channel.\", \"picture\": \"https://placekitten.com/400/400\"}'
|
||||
And Alice subscribes to channel_creation events
|
||||
And Alice receives a channel_creation event from Alice with content '{\"name\": \"Original\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}'
|
||||
And Alice subscribes to channel_metadata events
|
||||
Then Alice receives a channel_metadata event from Alice with content '{\"name\": \"Replaced\", \"about\": \"A different test channel.\", \"picture\": \"https://placekitten.com/400/400\"}'
|
72
test/integration/features/nip-28/nip-28.ts
Normal file
72
test/integration/features/nip-28/nip-28.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { Before, Then, When, World } from '@cucumber/cucumber'
|
||||
import WebSocket from 'ws'
|
||||
|
||||
import { createEvent, createSubscription, sendEvent, waitForNextEvent } from '../helpers'
|
||||
import { Event } from '../../../../src/@types/event'
|
||||
import { expect } from 'chai'
|
||||
|
||||
Before(function () {
|
||||
this.parameters.channels = []
|
||||
})
|
||||
|
||||
When(/^(\w+) sends a channel_creation event with content '([^']+)'$/, async function(name: string, content: string) {
|
||||
const ws = this.parameters.clients[name] as WebSocket
|
||||
const { pubkey, privkey } = this.parameters.identities[name]
|
||||
|
||||
const event: Event = await createEvent({ pubkey, kind: 40, content }, privkey)
|
||||
this.parameters.channels.push(event.id)
|
||||
await sendEvent(ws, event)
|
||||
this.parameters.events[name].push(event)
|
||||
})
|
||||
|
||||
When(/^(\w+) sends a channel_metadata event with content '([^']+)'$/, async function(name: string, content: string) {
|
||||
const ws = this.parameters.clients[name] as WebSocket
|
||||
const { pubkey, privkey } = this.parameters.identities[name]
|
||||
|
||||
const channel = this.parameters.channels[this.parameters.channels.length - 1]
|
||||
const event: Event = await createEvent({ pubkey, kind: 41, content, tags: [['e', channel]] }, privkey)
|
||||
|
||||
await sendEvent(ws, event)
|
||||
this.parameters.events[name].push(event)
|
||||
})
|
||||
|
||||
Then(/(\w+) receives a channel_creation event from (\w+) with content '([^']+?)'/, async function(name: string, author: string, content: string) {
|
||||
const ws = this.parameters.clients[name] as WebSocket
|
||||
const subscription = this.parameters.subscriptions[name][this.parameters.subscriptions[name].length - 1]
|
||||
const receivedEvent = await waitForNextEvent(ws, subscription.name)
|
||||
|
||||
expect(receivedEvent.kind).to.equal(40)
|
||||
expect(receivedEvent.pubkey).to.equal(this.parameters.identities[author].pubkey)
|
||||
expect(receivedEvent.content).to.equal(content)
|
||||
})
|
||||
|
||||
|
||||
Then(/(\w+) receives a channel_metadata event from (\w+) with content '([^']+?)'/, async function(name: string, author: string, content: string) {
|
||||
const ws = this.parameters.clients[name] as WebSocket
|
||||
const subscription = this.parameters.subscriptions[name][this.parameters.subscriptions[name].length - 1]
|
||||
const receivedEvent = await waitForNextEvent(ws, subscription.name)
|
||||
|
||||
const channel = this.parameters.channels[this.parameters.channels.length - 1]
|
||||
|
||||
expect(receivedEvent.kind).to.equal(41)
|
||||
expect(receivedEvent.pubkey).to.equal(this.parameters.identities[author].pubkey)
|
||||
expect(receivedEvent.content).to.equal(content)
|
||||
expect(receivedEvent.tags).to.deep.include(['e', channel])
|
||||
})
|
||||
|
||||
When(/^(\w+) subscribes to channel_creation events$/, async function(this: World<Record<string, any>>, name: string) {
|
||||
const ws = this.parameters.clients[name] as WebSocket
|
||||
const subscription = { name: `test-${Math.random()}`, filters: [{ kinds: [40] }] }
|
||||
this.parameters.subscriptions[name].push(subscription)
|
||||
|
||||
await createSubscription(ws, subscription.name, subscription.filters)
|
||||
})
|
||||
|
||||
|
||||
When(/^(\w+) subscribes to channel_metadata events$/, async function(this: World<Record<string, any>>, name: string) {
|
||||
const ws = this.parameters.clients[name] as WebSocket
|
||||
const subscription = { name: `test-${Math.random()}`, filters: [{ kinds: [41] }] }
|
||||
this.parameters.subscriptions[name].push(subscription)
|
||||
|
||||
await createSubscription(ws, subscription.name, subscription.filters)
|
||||
})
|
@ -459,7 +459,7 @@ describe('EventRepository', () => {
|
||||
|
||||
const query = repository.upsert(event).toString()
|
||||
|
||||
expect(query).to.equal('insert into "events" ("event_content", "event_created_at", "event_deduplication", "event_delegator", "event_id", "event_kind", "event_pubkey", "event_signature", "event_tags") values (\'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\', 1564498626, \'["55b702c167c85eb1c2d5ab35d68bedd1a35b94c01147364d2395c2f66f35a503",0]\', NULL, X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\', 0, X\'55b702c167c85eb1c2d5ab35d68bedd1a35b94c01147364d2395c2f66f35a503\', X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\', \'[]\') on conflict (event_pubkey, event_kind, event_deduplication) WHERE (event_kind = 0 OR event_kind = 3 OR (event_kind >= 10000 AND event_kind < 20000)) OR (event_kind >= 30000 AND event_kind < 40000) do update set "event_id" = X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\',"event_created_at" = 1564498626,"event_tags" = \'[]\',"event_content" = \'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\',"event_signature" = X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\',"event_delegator" = NULL where "events"."event_created_at" < 1564498626')
|
||||
expect(query).to.equal('insert into "events" ("event_content", "event_created_at", "event_deduplication", "event_delegator", "event_id", "event_kind", "event_pubkey", "event_signature", "event_tags") values (\'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\', 1564498626, \'["55b702c167c85eb1c2d5ab35d68bedd1a35b94c01147364d2395c2f66f35a503",0]\', NULL, X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\', 0, X\'55b702c167c85eb1c2d5ab35d68bedd1a35b94c01147364d2395c2f66f35a503\', X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\', \'[]\') on conflict (event_pubkey, event_kind, event_deduplication) WHERE (event_kind = 0 OR event_kind = 3 OR event_kind = 41 OR (event_kind >= 10000 AND event_kind < 20000)) OR (event_kind >= 30000 AND event_kind < 40000) do update set "event_id" = X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\',"event_created_at" = 1564498626,"event_tags" = \'[]\',"event_content" = \'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\',"event_signature" = X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\',"event_delegator" = NULL where "events"."event_created_at" < 1564498626')
|
||||
})
|
||||
|
||||
it('replaces event based on event_pubkey, event_kind and event_deduplication', () => {
|
||||
@ -476,7 +476,7 @@ describe('EventRepository', () => {
|
||||
|
||||
const query = repository.upsert(event).toString()
|
||||
|
||||
expect(query).to.equal('insert into "events" ("event_content", "event_created_at", "event_deduplication", "event_delegator", "event_id", "event_kind", "event_pubkey", "event_signature", "event_tags") values (\'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\', 1564498626, \'["deduplication"]\', NULL, X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\', 0, X\'55b702c167c85eb1c2d5ab35d68bedd1a35b94c01147364d2395c2f66f35a503\', X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\', \'[]\') on conflict (event_pubkey, event_kind, event_deduplication) WHERE (event_kind = 0 OR event_kind = 3 OR (event_kind >= 10000 AND event_kind < 20000)) OR (event_kind >= 30000 AND event_kind < 40000) do update set "event_id" = X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\',"event_created_at" = 1564498626,"event_tags" = \'[]\',"event_content" = \'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\',"event_signature" = X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\',"event_delegator" = NULL where "events"."event_created_at" < 1564498626')
|
||||
expect(query).to.equal('insert into "events" ("event_content", "event_created_at", "event_deduplication", "event_delegator", "event_id", "event_kind", "event_pubkey", "event_signature", "event_tags") values (\'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\', 1564498626, \'["deduplication"]\', NULL, X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\', 0, X\'55b702c167c85eb1c2d5ab35d68bedd1a35b94c01147364d2395c2f66f35a503\', X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\', \'[]\') on conflict (event_pubkey, event_kind, event_deduplication) WHERE (event_kind = 0 OR event_kind = 3 OR event_kind = 41 OR (event_kind >= 10000 AND event_kind < 20000)) OR (event_kind >= 30000 AND event_kind < 40000) do update set "event_id" = X\'e527fe8b0f64a38c6877f943a9e8841074056ba72aceb31a4c85e6d10b27095a\',"event_created_at" = 1564498626,"event_tags" = \'[]\',"event_content" = \'{"name":"ottman@minds.io","about":"","picture":"https://feat-2311-nostr.minds.io/icon/1002952989368913934/medium/1564498626/1564498626/1653379539"}\',"event_signature" = X\'d1de98733de2b412549aa64454722d9b66ab3c68e9e0d0f9c5d42e7bd54c30a06174364b683d2c8dbb386ff47f31e6cb7e2f3c3498d8819ee80421216c8309a9\',"event_delegator" = NULL where "events"."event_created_at" < 1564498626')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user