diff --git a/src/@types/event.ts b/src/@types/event.ts index 37c4c92..f3183e8 100644 --- a/src/@types/event.ts +++ b/src/@types/event.ts @@ -32,7 +32,7 @@ export interface DBEvent { event_delegator?: Buffer | null event_deduplication?: string | null first_seen: Date - deleted_at: Date + deleted_at?: Date } export interface CanonicalEvent { diff --git a/src/handlers/subscribe-message-handler.ts b/src/handlers/subscribe-message-handler.ts index aa37322..fdd2f13 100644 --- a/src/handlers/subscribe-message-handler.ts +++ b/src/handlers/subscribe-message-handler.ts @@ -2,12 +2,12 @@ import { anyPass, equals, map, uniqWith } from 'ramda' import { pipeline } from 'stream/promises' import { createEndOfStoredEventsNoticeMessage, createNoticeMessage, createOutgoingEventMessage } from '../utils/messages' +import { DBEvent, Event } from '../@types/event' import { IAbortable, IMessageHandler } from '../@types/message-handlers' import { isEventMatchingFilter, toNostrEvent } from '../utils/event' import { streamEach, streamEnd, streamFilter, streamMap } from '../utils/stream' import { SubscriptionFilter, SubscriptionId } from '../@types/subscription' import { createLogger } from '../factories/logger-factory' -import { Event } from '../@types/event' import { IEventRepository } from '../@types/repositories' import { ISettings } from '../@types/settings' import { IWebSocketAdapter } from '../@types/adapters' @@ -57,9 +57,12 @@ export class SubscribeMessageHandler implements IMessageHandler, IAbortable { const findEvents = this.eventRepository.findByFilters(filters).stream() + const isNotDeleted = (row: DBEvent) => { console.log(row); return true } + try { await pipeline( findEvents, + streamFilter(isNotDeleted), streamMap(toNostrEvent), streamFilter(isSubscribedToEvent), streamEach(sendEvent), diff --git a/src/repositories/event-repository.ts b/src/repositories/event-repository.ts index 022e7a2..03f7f71 100644 --- a/src/repositories/event-repository.ts +++ b/src/repositories/event-repository.ts @@ -232,9 +232,7 @@ export class EventRepository implements IEventRepository { public deleteByPubkeyAndIds(pubkey: string, ids: EventId[]): Promise { debug('deleting events from %s: %o', pubkey, ids) return this.dbClient('events') - .where({ - event_pubkey: toBuffer(pubkey), - }) + .where('event_pubkey', toBuffer(pubkey)) .whereIn('event_id', map(toBuffer)(ids)) .whereNull('deleted_at') .update({ diff --git a/test/integration/docker-compose.yml b/test/integration/docker-compose.yml index d99b595..965d467 100644 --- a/test/integration/docker-compose.yml +++ b/test/integration/docker-compose.yml @@ -17,6 +17,7 @@ services: REDIS_USER: default REDIS_PASSWORD: nostr_ts_relay_test NOSTR_CONFIG_DIR: /code + DEBUG: knex:query,primary:event-repository volumes: - ../../package.json:/code/package.json - ../../settings.sample.json:/code/settings.sample.json @@ -47,6 +48,8 @@ services: POSTGRES_DB: nostr_ts_relay_test networks: - nostream-test + ports: + - 25432:5432 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] timeout: 5s diff --git a/test/integration/features/nip-09/nip-09.feature b/test/integration/features/nip-09/nip-09.feature new file mode 100644 index 0000000..e954089 --- /dev/null +++ b/test/integration/features/nip-09/nip-09.feature @@ -0,0 +1,14 @@ +@Test +Feature: NIP-09 + Scenario: Charlie deletes an event + Given someone called Charlie + And someone called Bob + And Charlie sends a text_note event with content "Twitter > Nostr" + And Charlie subscribes to author Charlie + And Charlie receives a text_note event from Charlie with content "Twitter > Nostr" + And Charlie unsubscribes from author Charlie + When Charlie sends a delete event for their last event + And Charlie subscribes to author Charlie + And Charlie receives 1 delete event from Charlie and EOSE + Then Bob subscribes to author Charlie + Then Bob receives 1 delete event from Charlie and EOSE diff --git a/test/integration/features/nip-09/nip-09.feature.ts b/test/integration/features/nip-09/nip-09.feature.ts new file mode 100644 index 0000000..50c588d --- /dev/null +++ b/test/integration/features/nip-09/nip-09.feature.ts @@ -0,0 +1,37 @@ +import { Then, When } from '@cucumber/cucumber' +import { expect } from 'chai' +import WebSocket from 'ws' + +import { createEvent, sendEvent, waitForEventCount } from '../helpers' +import { Event } from '../../../../src/@types/event' +import { EventTags } from '../../../../src/constants/base' +import { Tag } from '../../../../src/@types/base' + +When(/^(\w+) sends a delete event for their last event$/, async function( + name: string, +) { + const ws = this.parameters.clients[name] as WebSocket + const { pubkey, privkey } = this.parameters.identities[name] + + const tags: Tag[] = [ + [EventTags.Event, this.parameters.events[name][this.parameters.events[name].length - 1].id], + ] + + const event: Event = await createEvent({ pubkey, kind: 5, content: '', tags }, privkey) + + console.log('event', event) + + await sendEvent(ws, event) + this.parameters.events[name].push(event) +}) + +Then( + /(\w+) receives (\d+) delete events? from (\w+) and EOSE/, + async function(name: string, count: string, author: string) { + const ws = this.parameters.clients[name] as WebSocket + const subscription = this.parameters.subscriptions[name][this.parameters.subscriptions[name].length - 1] + const [event] = await waitForEventCount(ws, subscription.name, Number(count), true) + + expect(event.kind).to.equal(5) + expect(event.pubkey).to.equal(this.parameters.identities[author].pubkey) +}) \ No newline at end of file