Remove relay package, move everything into net

This commit is contained in:
Jon Staab
2025-10-20 13:09:53 -07:00
parent 88650fb166
commit 0be540c0d2
42 changed files with 128 additions and 528 deletions
+24 -24
View File
@@ -1,7 +1,8 @@
import EventEmitter from "events"
import {describe, expect, it, vi, beforeEach, afterEach} from "vitest"
import {LocalRelay, Repository, LOCAL_RELAY_URL} from "@welshman/relay"
import {makeEvent} from "@welshman/util"
import {prep, getPubkey, makeSecret} from "@welshman/signer"
import {AdapterEvent, SocketAdapter, LocalAdapter, getAdapter} from "../src/adapter"
import {Repository, LOCAL_RELAY_URL} from "../src/repository"
import {ClientMessage, RelayMessage} from "../src/message"
import {Socket, SocketEvent} from "../src/socket"
import {Pool} from "../src/pool"
@@ -69,17 +70,13 @@ describe("SocketAdapter", () => {
})
describe("LocalAdapter", () => {
let relay: LocalRelay & EventEmitter
let repository: Repository
let adapter: LocalAdapter
beforeEach(() => {
const mockRelay = new EventEmitter()
Object.assign(mockRelay, {
send: vi.fn(),
removeAllListeners: vi.fn(),
})
relay = mockRelay as unknown as LocalRelay & EventEmitter
adapter = new LocalAdapter(relay)
repository = new Repository()
adapter = new LocalAdapter(repository)
vi.useFakeTimers()
})
afterEach(() => {
@@ -88,32 +85,35 @@ describe("LocalAdapter", () => {
})
it("should initialize with correct relay", () => {
expect(adapter.relay).toBe(relay)
expect(adapter.urls).toEqual([LOCAL_RELAY_URL])
expect(adapter.sockets).toEqual([])
})
it("should forward received messages", () => {
const receiveSpy = vi.fn()
const pubkey = getPubkey(makeSecret())
const event = prep(makeEvent(1), pubkey)
adapter.send(["REQ", "r1", {kinds: [1]}])
adapter.send(["REQ", "r2", {kinds: [2]}])
adapter.on(AdapterEvent.Receive, receiveSpy)
repository.publish(event)
const message: RelayMessage = ["EVENT", "123", {id: "123", kind: 1}]
relay.emit("*", ...message)
expect(receiveSpy).toHaveBeenCalledWith(message, LOCAL_RELAY_URL)
expect(receiveSpy).toHaveBeenCalledTimes(1)
expect(receiveSpy).toHaveBeenCalledWith(["EVENT", "r1", event], LOCAL_RELAY_URL)
})
it("should send messages to relay", () => {
const message: ClientMessage = ["EVENT", {id: "123", kind: 1}]
adapter.send(message)
it("should send messages to relay", async () => {
const publishSpy = vi.spyOn(repository, "publish")
const pubkey = getPubkey(makeSecret())
const event = prep(makeEvent(1), pubkey)
expect(relay.send).toHaveBeenCalledWith("EVENT", message[1])
})
adapter.send(["EVENT", event])
it("should cleanup properly", () => {
const removeListenersSpy = vi.spyOn(adapter, "removeAllListeners")
adapter.cleanup()
expect(removeListenersSpy).toHaveBeenCalled()
await vi.runAllTimersAsync()
expect(publishSpy).toHaveBeenCalledTimes(1)
expect(publishSpy).toHaveBeenCalledWith(event)
})
})
+314
View File
@@ -0,0 +1,314 @@
import {describe, it, vi, expect, beforeEach} from "vitest"
import {now, choice, range} from "@welshman/lib"
import {getAddress, makeEvent, TrustedEvent, DELETE, MUTES} from "@welshman/util"
import {Repository} from "../src/repository"
const randomHex = () =>
Array.from(range(0, 64))
.map(() => choice(Array.from("0123456789abcdef")))
.join("")
const createEvent = (kind: number, extra = {}) => ({
...makeEvent(kind),
pubkey: randomHex(),
id: randomHex(),
sig: "fake",
...extra,
})
describe("Repository", () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe("basic operations", () => {
let repo: Repository
beforeEach(() => {
repo = new Repository()
})
it("should publish and retrieve events", () => {
const event = createEvent(1)
expect(repo.publish(event)).toBe(true)
expect(repo.getEvent(event.id)).toEqual(event)
})
it("should not publish invalid events", () => {
const invalidEvent = {} as TrustedEvent
const result = repo.publish(invalidEvent)
expect(result).toBe(false)
})
it("should handle duplicate events", () => {
const event = createEvent(1)
expect(repo.publish(event)).toBe(true)
expect(repo.publish(event)).toBe(false)
})
it("should check if events exist", () => {
const event = createEvent(1)
repo.publish(event)
expect(repo.hasEvent(event)).toBe(true)
})
})
describe("replaceable events", () => {
let repo: Repository
beforeEach(() => {
repo = new Repository()
})
it("should handle replaceable events", () => {
const pubkey = randomHex()
const event1 = createEvent(MUTES, {created_at: now() - 100, pubkey})
const event2 = createEvent(MUTES, {created_at: now(), pubkey})
const address1 = getAddress(event1)
const address2 = getAddress(event2)
repo.publish(event1)
repo.publish(event2)
expect(repo.getEvent(event1.id)).toEqual(event1)
expect(repo.getEvent(address1)).toEqual(event2)
expect(repo.getEvent(event2.id)).toEqual(event2)
expect(repo.getEvent(address2)).toEqual(event2)
const event3 = createEvent(MUTES, {created_at: now() - 50, pubkey})
repo.publish(event3)
expect(repo.getEvent(event3.id)).toBeUndefined()
})
it("should not replace with older events", () => {
const event1 = createEvent(MUTES, {created_at: now()})
const event2 = createEvent(MUTES, {created_at: now() - 100})
repo.publish(event1)
repo.publish(event2)
expect(repo.getEvent(event1.id)).toEqual(event1)
})
})
describe("delete events", () => {
let repo: Repository
beforeEach(() => {
repo = new Repository()
})
it("should handle delete events", () => {
const event = createEvent(1)
const deleteEvent = createEvent(DELETE, {tags: [["e", event.id]], created_at: now() + 100})
repo.publish(event)
repo.publish(deleteEvent)
expect(repo.isDeleted(event)).toBe(true)
})
it("should handle delete by address", () => {
const event = createEvent(MUTES)
const deleteEvent = createEvent(DELETE, {
tags: [["a", `10000:${event.pubkey}:`]],
created_at: now() + 100,
})
repo.publish(event)
repo.publish(deleteEvent)
expect(repo.isDeletedByAddress(event)).toBe(true)
})
})
describe("expire events", () => {
let repo: Repository
beforeEach(() => {
repo = new Repository()
})
it("should handle expiring events", () => {
const event1 = createEvent(1, {tags: [["expiration", String(now() - 100)]]})
const event2 = createEvent(1, {tags: [["expiration", String(now() + 100)]]})
const event3 = createEvent(1)
repo.publish(event1)
repo.publish(event2)
repo.publish(event3)
expect(repo.isExpired(event1)).toBe(true)
expect(repo.isExpired(event2)).toBe(false)
expect(repo.isExpired(event3)).toBe(false)
})
})
describe("query operations", () => {
let repo: Repository
beforeEach(() => {
repo = new Repository()
})
it("should throw on invalid queries", () => {
expect(() => repo.query([{limit: 10}], {shouldSort: false})).toThrow()
})
it("should query by ids", () => {
const event = createEvent(1)
repo.publish(event)
const results = repo.query([{ids: [event.id]}])
expect(results).toContain(event)
})
it("should query by authors", () => {
const event = createEvent(1)
repo.publish(event)
const results = repo.query([{authors: [event.pubkey]}])
expect(results).toContain(event)
})
it("should query by kinds", () => {
const event = createEvent(1)
repo.publish(event)
const results = repo.query([{kinds: [1]}])
expect(results).toContain(event)
})
it("should query by tags", () => {
const pubkey = randomHex()
const event = createEvent(1, {tags: [["p", pubkey]]})
repo.publish(event)
const results = repo.query([{"#p": [pubkey]}])
expect(results).toContain(event)
})
it("should query by time range", () => {
const event = createEvent(1)
repo.publish(event)
const results = repo.query([
{
since: now() - 3600,
until: now() + 3600,
},
])
expect(results).toContain(event)
})
it("should handle multiple filters", () => {
const event = createEvent(1)
repo.publish(event)
const results = repo.query([{kinds: [1]}, {authors: [event.pubkey]}])
expect(results).toHaveLength(1)
expect(results).toContain(event)
})
it("should respect limit parameter", () => {
const events = [
createEvent(1, {created_at: now()}),
createEvent(1, {created_at: now() - 100}),
]
events.forEach(e => repo.publish(e))
const results = repo.query([{limit: 1}])
expect(results).toHaveLength(1)
expect(results[0]).toEqual(events[0]) // Most recent event
})
it("should not return deleted events", () => {
const event = createEvent(1)
const deleteEvent = createEvent(DELETE, {tags: [["e", event.id]], created_at: now() + 1})
repo.publish(event)
repo.publish(deleteEvent)
const results = repo.query([{kinds: [1]}])
expect(results).not.toContain(event)
})
})
describe("dump and load", () => {
let repo: Repository
beforeEach(() => {
repo = new Repository()
})
it("should dump all events", () => {
const event = createEvent(1)
repo.publish(event)
const dumped = repo.dump()
expect(dumped).toContain(event)
})
it("should load events", () => {
const event = createEvent(1)
repo.load([event])
expect(repo.getEvent(event.id)).toEqual(event)
})
it("should handle chunked loading", () => {
const events = Array.from({length: 1500}, (_, i) => createEvent(1))
repo.load(events, 500)
expect(repo.dump()).toHaveLength(1500)
})
it("should emit update events", () => {
const event = createEvent(1)
const updateHandler = vi.fn()
repo.on("update", updateHandler)
repo.load([event])
expect(updateHandler).toHaveBeenCalledWith({
added: [event],
removed: new Set(),
})
})
})
describe("event removal", () => {
let repo: Repository
beforeEach(() => {
repo = new Repository()
})
it("should remove events", () => {
const event = createEvent(1)
repo.publish(event)
repo.removeEvent(event.id)
expect(repo.getEvent(event.id)).toBeUndefined()
})
it("should emit update on removal", () => {
const event = createEvent(1)
const updateHandler = vi.fn()
repo.on("update", updateHandler)
repo.publish(event)
repo.removeEvent(event.id)
expect(updateHandler).toHaveBeenLastCalledWith({
added: [],
removed: [event.id],
})
})
})
})