Bring back typedoc
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# @welshman/store [](https://npmjs.com/package/@welshman/store)
|
||||
# @welshman/app [](https://npmjs.com/package/@welshman/app)
|
||||
|
||||
Utilities for dealing with svelte stores when using welshman.
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ describe("collection", () => {
|
||||
getKey: item => item.id,
|
||||
})
|
||||
|
||||
const derived = col.deriveItem(null)
|
||||
const derived = col.deriveItem(undefined)
|
||||
expect(get(derived)).toBeUndefined()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {now} from "@welshman/lib"
|
||||
import {publish, PublishStatus, MockAdapter} from "@welshman/net"
|
||||
import {PublishStatus, MockAdapter} from "@welshman/net"
|
||||
import {NOTE, makeEvent} from "@welshman/util"
|
||||
import {Nip01Signer} from "@welshman/signer"
|
||||
import {LOCAL_RELAY_URL} from "@welshman/relay"
|
||||
@@ -112,7 +112,7 @@ describe("thunk", () => {
|
||||
const send = vi.fn()
|
||||
const track = vi.spyOn(tracker, 'track')
|
||||
const thunk = makeThunk(mockRequest)
|
||||
let status = {}
|
||||
let status: Record<string, any> = {}
|
||||
|
||||
// Subscribe to status updates
|
||||
thunk.status.subscribe(_status => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {describe, it, expect} from "vitest"
|
||||
import * as Content from "../src"
|
||||
import {ParsedLink, ParsedType, parse, reduceLinks, renderAsHtml, renderAsText} from "../src"
|
||||
import {npubEncode, noteEncode} from "nostr-tools/nip19"
|
||||
|
||||
describe("Content Parsing", () => {
|
||||
@@ -7,51 +7,55 @@ describe("Content Parsing", () => {
|
||||
const nevent = noteEncode("ff".repeat(32))
|
||||
describe("Basic Parsing", () => {
|
||||
it("should parse plain text", () => {
|
||||
const result = Content.parse({content: "Hello world"})
|
||||
const result = parse({content: "Hello world"})
|
||||
expect(result).toEqual([
|
||||
{type: Content.ParsedType.Text, value: "Hello world", raw: "Hello world"},
|
||||
{type: ParsedType.Text, value: "Hello world", raw: "Hello world"},
|
||||
])
|
||||
})
|
||||
|
||||
it("should parse newlines", () => {
|
||||
const result = Content.parse({content: "Hello\nworld"})
|
||||
const result = parse({content: "Hello\nworld"})
|
||||
expect(result).toEqual([
|
||||
{type: Content.ParsedType.Text, value: "Hello", raw: "Hello"},
|
||||
{type: Content.ParsedType.Newline, value: "\n", raw: "\n"},
|
||||
{type: Content.ParsedType.Text, value: "world", raw: "world"},
|
||||
{type: ParsedType.Text, value: "Hello", raw: "Hello"},
|
||||
{type: ParsedType.Newline, value: "\n", raw: "\n"},
|
||||
{type: ParsedType.Text, value: "world", raw: "world"},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("Link Parsing", () => {
|
||||
it("should parse basic URLs", () => {
|
||||
const result = Content.parse({content: "Check https://example.com"})
|
||||
expect(result[1]).toMatchObject({
|
||||
type: Content.ParsedType.Link,
|
||||
const result = parse({content: "Check https://example.com"})
|
||||
const parsed = result[1] as ParsedLink
|
||||
|
||||
expect(parsed).toMatchObject({
|
||||
type: ParsedType.Link,
|
||||
value: {
|
||||
url: expect.any(URL),
|
||||
// isMedia: false,
|
||||
},
|
||||
})
|
||||
expect(result[1].value.url.toString()).toBe("https://example.com/")
|
||||
|
||||
expect(parsed.value.url.toString()).toBe("https://example.com/")
|
||||
})
|
||||
|
||||
it("should parse URLs without protocol", () => {
|
||||
const result = Content.parse({content: "Visit example.com"})
|
||||
expect(result[1]).toMatchObject({
|
||||
type: Content.ParsedType.Link,
|
||||
const result = parse({content: "Visit example.com"})
|
||||
const parsed = result[1] as ParsedLink
|
||||
|
||||
expect(parsed).toMatchObject({
|
||||
type: ParsedType.Link,
|
||||
value: {
|
||||
url: expect.any(URL),
|
||||
// isMedia: false,
|
||||
},
|
||||
})
|
||||
expect(result[1].value.url.toString()).toBe("https://example.com/")
|
||||
|
||||
expect(parsed.value.url.toString()).toBe("https://example.com/")
|
||||
})
|
||||
|
||||
it("should identify media links", () => {
|
||||
const result = Content.parse({content: "https://example.com/image.jpg"})
|
||||
const result = parse({content: "https://example.com/image.jpg"})
|
||||
expect(result[0]).toMatchObject({
|
||||
type: Content.ParsedType.Link,
|
||||
type: ParsedType.Link,
|
||||
value: {
|
||||
url: expect.any(URL),
|
||||
meta: {},
|
||||
@@ -62,46 +66,46 @@ describe("Content Parsing", () => {
|
||||
|
||||
describe("Nostr Entity Parsing", () => {
|
||||
it("should parse nostr profiles", () => {
|
||||
const result = Content.parse({
|
||||
const result = parse({
|
||||
content: `nostr:${npub}`,
|
||||
})
|
||||
|
||||
expect(result[0]).toMatchObject({
|
||||
type: Content.ParsedType.Profile,
|
||||
type: ParsedType.Profile,
|
||||
})
|
||||
})
|
||||
|
||||
it("should parse nostr events", () => {
|
||||
const result = Content.parse({
|
||||
const result = parse({
|
||||
content: `nostr:${nevent}`,
|
||||
})
|
||||
expect(result[0]).toMatchObject({
|
||||
type: Content.ParsedType.Event,
|
||||
type: ParsedType.Event,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Special Content Parsing", () => {
|
||||
it("should parse code blocks", () => {
|
||||
const result = Content.parse({content: "```const x = 1```"})
|
||||
const result = parse({content: "```const x = 1```"})
|
||||
expect(result[0]).toMatchObject({
|
||||
type: Content.ParsedType.Code,
|
||||
type: ParsedType.Code,
|
||||
value: "const x = 1",
|
||||
})
|
||||
})
|
||||
|
||||
it("should parse inline code", () => {
|
||||
const result = Content.parse({content: "Use `npm install`"})
|
||||
const result = parse({content: "Use `npm install`"})
|
||||
expect(result[1]).toMatchObject({
|
||||
type: Content.ParsedType.Code,
|
||||
type: ParsedType.Code,
|
||||
value: "npm install",
|
||||
})
|
||||
})
|
||||
|
||||
it("should parse topics", () => {
|
||||
const result = Content.parse({content: "#nostr is cool"})
|
||||
const result = parse({content: "#nostr is cool"})
|
||||
expect(result[0]).toMatchObject({
|
||||
type: Content.ParsedType.Topic,
|
||||
type: ParsedType.Topic,
|
||||
value: "nostr",
|
||||
})
|
||||
})
|
||||
@@ -109,27 +113,27 @@ describe("Content Parsing", () => {
|
||||
|
||||
describe("Rendering", () => {
|
||||
it("should render as text", () => {
|
||||
const parsed = Content.parse({content: "Hello https://example.com"})
|
||||
const rendered = Content.renderAsText(parsed).toString()
|
||||
const parsed = parse({content: "Hello https://example.com"})
|
||||
const rendered = renderAsText(parsed).toString()
|
||||
expect(rendered).toContain("Hello")
|
||||
expect(rendered).toContain("https://example.com")
|
||||
})
|
||||
|
||||
it("should render as HTML", () => {
|
||||
const parsed = Content.parse({content: "Hello https://example.com"})
|
||||
const rendered = Content.renderAsHtml(parsed).toString()
|
||||
const parsed = parse({content: "Hello https://example.com"})
|
||||
const rendered = renderAsHtml(parsed).toString()
|
||||
expect(rendered).toContain('<a href="https://example.com/"')
|
||||
})
|
||||
})
|
||||
|
||||
describe("Link Grid", () => {
|
||||
it("should reduce consecutive image links into a grid", () => {
|
||||
const content = Content.parse({
|
||||
const content = parse({
|
||||
content: "https://example.com/1.jpg\nhttps://example.com/2.jpg https://example.com/2.jpg",
|
||||
})
|
||||
const reduced = Content.reduceLinks(content)
|
||||
const reduced = reduceLinks(content)
|
||||
expect(reduced[0]).toMatchObject({
|
||||
type: Content.ParsedType.LinkGrid,
|
||||
type: ParsedType.LinkGrid,
|
||||
value: {
|
||||
links: expect.any(Array),
|
||||
},
|
||||
@@ -139,12 +143,12 @@ describe("Content Parsing", () => {
|
||||
|
||||
describe("Legacy Mention Parsing", () => {
|
||||
it("should parse legacy mentions", () => {
|
||||
const result = Content.parse({
|
||||
const result = parse({
|
||||
content: "#[0]",
|
||||
tags: [["p", "1234567890"]],
|
||||
})
|
||||
expect(result[0]).toMatchObject({
|
||||
type: Content.ParsedType.Profile,
|
||||
type: ParsedType.Profile,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -36,7 +36,6 @@ describe("Content Truncation", () => {
|
||||
value: {
|
||||
url: new URL("https://example.com/image.jpg"),
|
||||
meta: {},
|
||||
isMedia: true,
|
||||
},
|
||||
raw: "https://example.com/image.jpg",
|
||||
},
|
||||
@@ -83,7 +82,6 @@ describe("Content Truncation", () => {
|
||||
value: {
|
||||
url: new URL("https://example.com/image.jpg"),
|
||||
meta: {},
|
||||
isMedia: true,
|
||||
},
|
||||
raw: "https://example.com/image.jpg",
|
||||
},
|
||||
@@ -141,8 +139,8 @@ describe("Content Truncation", () => {
|
||||
type: ParsedType.LinkGrid,
|
||||
value: {
|
||||
links: [
|
||||
{url: new URL("https://example.com/1.jpg"), meta: {}, isMedia: true},
|
||||
{url: new URL("https://example.com/2.jpg"), meta: {}, isMedia: true},
|
||||
{url: new URL("https://example.com/1.jpg"), meta: {}},
|
||||
{url: new URL("https://example.com/2.jpg"), meta: {}},
|
||||
],
|
||||
},
|
||||
raw: "",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {defaultTagFeedMappings} from "@welshman/feeds"
|
||||
import {now} from "@welshman/lib"
|
||||
import {getAddress, type TrustedEvent} from "@welshman/util"
|
||||
import {getAddress, TrustedEvent} from "@welshman/util"
|
||||
import {beforeEach, describe, expect, it, vi} from "vitest"
|
||||
import {FeedCompiler} from "../src/compiler"
|
||||
import {Feed, FeedType, Scope} from "../src/core"
|
||||
@@ -269,7 +269,7 @@ describe("FeedCompiler", () => {
|
||||
sig: "sig1",
|
||||
}
|
||||
|
||||
mockOptions.requestDVM.mockImplementation(async ({onEvent}) => {
|
||||
mockOptions.requestDVM.mockImplementation(async ({onEvent}: any) => {
|
||||
await onEvent(mockEvent)
|
||||
})
|
||||
|
||||
@@ -307,7 +307,7 @@ describe("FeedCompiler", () => {
|
||||
sig: "sig1",
|
||||
}
|
||||
|
||||
mockOptions.request.mockImplementation(({onEvent}) => {
|
||||
mockOptions.request.mockImplementation(({onEvent}: any) => {
|
||||
onEvent(mockEvent)
|
||||
})
|
||||
|
||||
@@ -345,7 +345,7 @@ describe("FeedCompiler", () => {
|
||||
sig: "sig1",
|
||||
}
|
||||
|
||||
mockOptions.request.mockImplementation(({onEvent}) => {
|
||||
mockOptions.request.mockImplementation(({onEvent}: any) => {
|
||||
onEvent(labelEvent)
|
||||
})
|
||||
|
||||
@@ -381,7 +381,7 @@ describe("FeedCompiler", () => {
|
||||
sig: "sig1",
|
||||
}
|
||||
|
||||
mockOptions.requestDVM.mockImplementation(async ({onEvent}) => {
|
||||
mockOptions.requestDVM.mockImplementation(async ({onEvent}: any) => {
|
||||
await onEvent(mockEvent)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Tracker} from "../src/Tracker"
|
||||
import {vi, describe, it, expect, beforeEach} from "vitest"
|
||||
import {Tracker} from "../src/tracker"
|
||||
|
||||
describe("Tracker", () => {
|
||||
let tracker: Tracker
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import EventEmitter from "events"
|
||||
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
|
||||
import { isRelayUrl } from "@welshman/util"
|
||||
import { LocalRelay, LOCAL_RELAY_URL } from "@welshman/relay"
|
||||
import { LocalRelay, Repository, LOCAL_RELAY_URL } from "@welshman/relay"
|
||||
import { AdapterEvent, SocketAdapter, LocalAdapter, getAdapter } from "../src/adapter"
|
||||
import { ClientMessage, RelayMessage } from "../src/message"
|
||||
import { Socket, SocketEvent } from "../src/socket"
|
||||
import { Pool } from "../src/pool"
|
||||
|
||||
vi.mock('isomorphic-ws', () => {
|
||||
const WebSocket = vi.fn(function () {
|
||||
const WebSocket = vi.fn(function (this: any) {
|
||||
setTimeout(() => this.onopen())
|
||||
})
|
||||
|
||||
WebSocket.prototype.send = vi.fn()
|
||||
|
||||
WebSocket.prototype.close = vi.fn(function () {
|
||||
WebSocket.prototype.close = vi.fn(function (this: any) {
|
||||
this.onclose()
|
||||
})
|
||||
|
||||
@@ -120,12 +120,14 @@ describe("LocalAdapter", () => {
|
||||
|
||||
describe("getAdapter", () => {
|
||||
let pool: Pool
|
||||
let relay: LocalRelay
|
||||
let repository: Repository
|
||||
|
||||
beforeEach(() => {
|
||||
pool = new Pool()
|
||||
relay = new LocalRelay()
|
||||
pool.get = vi.fn().mockReturnValue(new Socket("wss://test.relay"))
|
||||
pool = new Pool({
|
||||
makeSocket: () => new Socket("wss://test.relay"),
|
||||
})
|
||||
|
||||
repository = new Repository()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -134,7 +136,7 @@ describe("getAdapter", () => {
|
||||
|
||||
it("should return LocalAdapter for local relay URL", () => {
|
||||
const url = LOCAL_RELAY_URL
|
||||
const adapter = getAdapter(url, { relay })
|
||||
const adapter = getAdapter(url, { repository })
|
||||
expect(adapter).toBeInstanceOf(LocalAdapter)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
|
||||
import { Socket, SocketStatus, SocketEvent } from "../src/socket"
|
||||
import { makeEvent, CLIENT_AUTH } from "@welshman/util"
|
||||
import { makeEvent, StampedEvent, CLIENT_AUTH } from "@welshman/util"
|
||||
import { Nip01Signer } from "@welshman/signer"
|
||||
import { AuthState, AuthStatus, AuthStateEvent, makeAuthEvent } from "../src/auth"
|
||||
import EventEmitter from "events"
|
||||
@@ -146,7 +146,7 @@ describe('auth', () => {
|
||||
socket.auth.challenge = "challenge123"
|
||||
socket.auth.status = AuthStatus.Requested
|
||||
|
||||
const sign = async e => {
|
||||
const sign = async (e: StampedEvent) => {
|
||||
event = await Nip01Signer.ephemeral().sign(e)
|
||||
|
||||
return event
|
||||
@@ -154,7 +154,7 @@ describe('auth', () => {
|
||||
|
||||
await socket.auth.authenticate(sign)
|
||||
|
||||
expect(socket.auth.request).toStrictEqual(event.id)
|
||||
expect(socket.auth.request).toStrictEqual(event!.id)
|
||||
expect(sendSpy).toHaveBeenCalledWith(["AUTH", event])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,13 +4,13 @@ import { Pool, makeSocket } from "../src/pool"
|
||||
import { normalizeRelayUrl } from "@welshman/util"
|
||||
|
||||
vi.mock('isomorphic-ws', () => {
|
||||
const WebSocket = vi.fn(function () {
|
||||
const WebSocket = vi.fn(function (this: any) {
|
||||
setTimeout(() => this.onopen())
|
||||
})
|
||||
|
||||
WebSocket.prototype.send = vi.fn()
|
||||
|
||||
WebSocket.prototype.close = vi.fn(function () {
|
||||
WebSocket.prototype.close = vi.fn(function (this: any) {
|
||||
this.onclose()
|
||||
})
|
||||
|
||||
@@ -97,7 +97,7 @@ describe("Pool", () => {
|
||||
it("should remove and cleanup existing socket", () => {
|
||||
const mockSocket = { url: "wss://test.relay", cleanup: vi.fn() }
|
||||
|
||||
pool._data.set(mockSocket.url, mockSocket)
|
||||
pool._data.set(mockSocket.url, mockSocket as unknown as Socket)
|
||||
pool.remove(mockSocket.url)
|
||||
|
||||
expect(mockSocket.cleanup).toHaveBeenCalled()
|
||||
@@ -116,7 +116,7 @@ describe("Pool", () => {
|
||||
const mockSockets = urls.map(url => ({ url, cleanup: vi.fn() }))
|
||||
|
||||
for (const mockSocket of mockSockets) {
|
||||
pool._data.set(mockSocket.url, mockSocket)
|
||||
pool._data.set(mockSocket.url, mockSocket as unknown as Socket)
|
||||
}
|
||||
|
||||
pool.clear()
|
||||
|
||||
@@ -103,14 +103,14 @@ describe("SinglePublish", () => {
|
||||
pub.on(PublishEvent.Complete, completeSpy)
|
||||
pub.on(PublishEvent.Timeout, timeoutSpy)
|
||||
|
||||
await vi.runAllTimers(200)
|
||||
await vi.runAllTimers()
|
||||
|
||||
expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event])
|
||||
|
||||
await vi.runAllTimers()
|
||||
|
||||
expect(successSpy).not.toHaveBeenCalled()
|
||||
expect(failureSpy).not.toHaveBeenCalled(event.id, "hi")
|
||||
expect(failureSpy).not.toHaveBeenCalled()
|
||||
expect(completeSpy).toHaveBeenCalled()
|
||||
expect(timeoutSpy).toHaveBeenCalled()
|
||||
})
|
||||
@@ -137,7 +137,7 @@ describe("SinglePublish", () => {
|
||||
pub.on(PublishEvent.Complete, completeSpy)
|
||||
pub.on(PublishEvent.Timeout, abortSpy)
|
||||
|
||||
await vi.runAllTimers(200)
|
||||
await vi.runAllTimers()
|
||||
|
||||
expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event])
|
||||
|
||||
@@ -146,7 +146,7 @@ describe("SinglePublish", () => {
|
||||
await vi.runAllTimers()
|
||||
|
||||
expect(successSpy).not.toHaveBeenCalled()
|
||||
expect(failureSpy).not.toHaveBeenCalled(event.id, "hi")
|
||||
expect(failureSpy).not.toHaveBeenCalled()
|
||||
expect(completeSpy).toHaveBeenCalled()
|
||||
expect(abortSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -5,13 +5,13 @@ import { Socket, SocketStatus, SocketEvent } from "../src/socket"
|
||||
import { ClientMessage, RelayMessage } from "../src/message"
|
||||
|
||||
vi.mock('isomorphic-ws', () => {
|
||||
const WebSocket = vi.fn(function () {
|
||||
const WebSocket = vi.fn(function (this: any) {
|
||||
setTimeout(() => this.onopen())
|
||||
})
|
||||
|
||||
WebSocket.prototype.send = vi.fn()
|
||||
|
||||
WebSocket.prototype.close = vi.fn(function () {
|
||||
WebSocket.prototype.close = vi.fn(function (this: any) {
|
||||
this.onclose()
|
||||
})
|
||||
|
||||
@@ -77,11 +77,9 @@ describe("Socket", () => {
|
||||
|
||||
socket.open()
|
||||
|
||||
const ws = socket._ws
|
||||
|
||||
socket.close()
|
||||
|
||||
expect(ws.close).toHaveBeenCalled()
|
||||
expect(socket._ws!.close).toHaveBeenCalled()
|
||||
expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Closed, "wss://test.relay")
|
||||
})
|
||||
})
|
||||
@@ -102,14 +100,14 @@ describe("Socket", () => {
|
||||
socket.on(SocketEvent.Send, sendSpy)
|
||||
|
||||
socket.open()
|
||||
socket._ws.onopen()
|
||||
socket._ws?.onopen?.(undefined as unknown as any)
|
||||
|
||||
const message: ClientMessage = ["EVENT", { id: "123", kind: 1 }]
|
||||
socket.send(message)
|
||||
|
||||
await vi.runAllTimers()
|
||||
|
||||
expect(socket._ws.send).toHaveBeenCalledWith(JSON.stringify(message))
|
||||
expect(socket._ws!.send).toHaveBeenCalledWith(JSON.stringify(message))
|
||||
expect(sendSpy).toHaveBeenCalledWith(message, "wss://test.relay")
|
||||
})
|
||||
})
|
||||
@@ -121,9 +119,8 @@ describe("Socket", () => {
|
||||
|
||||
socket.open()
|
||||
const message: RelayMessage = ["EVENT", "123", { id: "123", kind: 1 }]
|
||||
socket._ws.onmessage({ data: JSON.stringify(message) })
|
||||
socket._ws?.onmessage?.({data: JSON.stringify(message)} as unknown as any)
|
||||
|
||||
// Allow task queue to process
|
||||
await vi.runAllTimers()
|
||||
|
||||
expect(receiveSpy).toHaveBeenCalledWith(message, "wss://test.relay")
|
||||
@@ -134,7 +131,7 @@ describe("Socket", () => {
|
||||
socket.on(SocketEvent.Error, errorSpy)
|
||||
|
||||
socket.open()
|
||||
socket._ws.onmessage({ data: "invalid json" })
|
||||
socket._ws?.onmessage?.({data: "invalid json"} as unknown as any)
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith("Invalid message received", "wss://test.relay")
|
||||
})
|
||||
@@ -144,7 +141,7 @@ describe("Socket", () => {
|
||||
socket.on(SocketEvent.Error, errorSpy)
|
||||
|
||||
socket.open()
|
||||
socket._ws.onmessage({ data: JSON.stringify({ not: "an array" }) })
|
||||
socket._ws?.onmessage?.({data: JSON.stringify({not: "an array"})} as unknown as any)
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith("Invalid message received", "wss://test.relay")
|
||||
})
|
||||
@@ -158,7 +155,7 @@ describe("Socket", () => {
|
||||
|
||||
socket.cleanup()
|
||||
|
||||
expect(ws.close).toHaveBeenCalled()
|
||||
expect(ws!.close).toHaveBeenCalled()
|
||||
expect(socket.listenerCount(SocketEvent.Send)).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -169,7 +166,7 @@ describe("Socket", () => {
|
||||
socket.on(SocketEvent.Status, statusSpy)
|
||||
|
||||
socket.open()
|
||||
socket._ws.onerror()
|
||||
socket._ws?.onerror?.(undefined as unknown as any)
|
||||
|
||||
expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Error, "wss://test.relay")
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {describe, it, vi, expect, beforeEach} from "vitest"
|
||||
import {now} from "@welshman/lib"
|
||||
import {getAddress, TrustedEvent, DELETE, MUTES} from "@welshman/util"
|
||||
import {Repository} from "../src/Repository"
|
||||
import {Repository} from "../src/repository"
|
||||
|
||||
describe("Repository", () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {Repository, TrustedEvent} from "@welshman/util"
|
||||
import {TrustedEvent} from "@welshman/util"
|
||||
import {Repository} from "@welshman/relay"
|
||||
import {get} from "svelte/store"
|
||||
import {afterEach, beforeEach, describe, expect, it, vi} from "vitest"
|
||||
import {
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
import {describe, it, vi, expect, beforeEach} from "vitest"
|
||||
import * as Filters from "../src/Filters"
|
||||
|
||||
import type {TrustedEvent} from "../src/Events"
|
||||
import {GENERIC_REPOST, LONG_FORM, MUTES, REPOST} from "@welshman/util"
|
||||
import {
|
||||
addRepostFilters,
|
||||
getFilterGenerality,
|
||||
getFilterId,
|
||||
getFilterResultCardinality,
|
||||
getIdFilters,
|
||||
getReplyFilters,
|
||||
guessFilterDelta,
|
||||
intersectFilters,
|
||||
matchFilter,
|
||||
matchFilters,
|
||||
trimFilter,
|
||||
unionFilters,
|
||||
Filter,
|
||||
} from "../src/Filters"
|
||||
import type {TrustedEvent} from "../src/Events"
|
||||
|
||||
describe("Filters", () => {
|
||||
beforeEach(() => {
|
||||
@@ -27,23 +40,23 @@ describe("Filters", () => {
|
||||
it("should match basic filter criteria", () => {
|
||||
const event = createEvent()
|
||||
const filter = {kinds: [1], authors: [pubkey]}
|
||||
expect(Filters.matchFilter(filter, event)).toBe(true)
|
||||
expect(matchFilter(filter, event)).toBe(true)
|
||||
})
|
||||
|
||||
it("should handle search terms", () => {
|
||||
const event = createEvent({content: "Hello Nostr World!"})
|
||||
expect(Filters.matchFilter({search: "nostr"}, event)).toBe(true)
|
||||
expect(Filters.matchFilter({search: "bitcoin"}, event)).toBe(false)
|
||||
expect(matchFilter({search: "nostr"}, event)).toBe(true)
|
||||
expect(matchFilter({search: "bitcoin"}, event)).toBe(false)
|
||||
})
|
||||
|
||||
it("should handle multiple search terms", () => {
|
||||
const event = createEvent({content: "Hello Nostr World!"})
|
||||
expect(Filters.matchFilter({search: "hello world"}, event)).toBe(true)
|
||||
expect(matchFilter({search: "hello world"}, event)).toBe(true)
|
||||
})
|
||||
|
||||
it("should handle case-insensitive search", () => {
|
||||
const event = createEvent({content: "Hello NOSTR World!"})
|
||||
expect(Filters.matchFilter({search: "nostr"}, event)).toBe(true)
|
||||
expect(matchFilter({search: "nostr"}, event)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -51,13 +64,13 @@ describe("Filters", () => {
|
||||
it("should match if any filter matches", () => {
|
||||
const event = createEvent()
|
||||
const filters = [{kinds: [2]}, {kinds: [1], authors: [pubkey]}]
|
||||
expect(Filters.matchFilters(filters, event)).toBe(true)
|
||||
expect(matchFilters(filters, event)).toBe(true)
|
||||
})
|
||||
|
||||
it("should not match if no filters match", () => {
|
||||
const event = createEvent()
|
||||
const filters = [{kinds: [2]}, {kinds: [3]}]
|
||||
expect(Filters.matchFilters(filters, event)).toBe(false)
|
||||
expect(matchFilters(filters, event)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -65,13 +78,13 @@ describe("Filters", () => {
|
||||
it("should generate consistent IDs for equivalent filters", () => {
|
||||
const filter1 = {kinds: [1], authors: [pubkey]}
|
||||
const filter2 = {authors: [pubkey], kinds: [1]}
|
||||
expect(Filters.getFilterId(filter1)).toBe(Filters.getFilterId(filter2))
|
||||
expect(getFilterId(filter1)).toBe(getFilterId(filter2))
|
||||
})
|
||||
|
||||
it("should generate different IDs for different filters", () => {
|
||||
const filter1 = {kinds: [1], authors: [pubkey]}
|
||||
const filter2 = {kinds: [2], authors: [pubkey]}
|
||||
expect(Filters.getFilterId(filter1)).not.toBe(Filters.getFilterId(filter2))
|
||||
expect(getFilterId(filter1)).not.toBe(getFilterId(filter2))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -81,14 +94,14 @@ describe("Filters", () => {
|
||||
{kinds: [1], authors: [pubkey]},
|
||||
{kinds: [1], authors: [pubkey + "1"]},
|
||||
]
|
||||
const result = Filters.unionFilters(filters)
|
||||
const result = unionFilters(filters)
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].authors).toHaveLength(2)
|
||||
})
|
||||
|
||||
it("should handle different filter groups", () => {
|
||||
const filters = [{kinds: [1]}, {"#e": [id]}]
|
||||
const result = Filters.unionFilters(filters)
|
||||
const filters: Filter[] = [{kinds: [1]}, {"#e": [id]}]
|
||||
const result = unionFilters(filters)
|
||||
expect(result).toHaveLength(2)
|
||||
})
|
||||
|
||||
@@ -97,7 +110,7 @@ describe("Filters", () => {
|
||||
{kinds: [1], limit: 10, since: 1000, until: 2000, search: "test"},
|
||||
{kinds: [1], limit: 10, since: 1000, until: 2000, search: "test"},
|
||||
]
|
||||
const result = Filters.unionFilters(filters)
|
||||
const result = unionFilters(filters)
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0]).toMatchObject({limit: 10, since: 1000, until: 2000, search: "test"})
|
||||
})
|
||||
@@ -106,7 +119,7 @@ describe("Filters", () => {
|
||||
describe("intersectFilters", () => {
|
||||
it("should combine filter groups", () => {
|
||||
const groups = [[{kinds: [1]}], [{authors: [pubkey]}]]
|
||||
const result = Filters.intersectFilters(groups)
|
||||
const result = intersectFilters(groups)
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0]).toMatchObject({
|
||||
kinds: [1],
|
||||
@@ -119,7 +132,7 @@ describe("Filters", () => {
|
||||
[{since: 1000, until: 2000, limit: 10}],
|
||||
[{since: 1500, until: 1800, limit: 20}],
|
||||
]
|
||||
const result = Filters.intersectFilters(groups)
|
||||
const result = intersectFilters(groups)
|
||||
expect(result[0]).toMatchObject({
|
||||
since: 1500, // Max of since
|
||||
until: 1800, // Min of until
|
||||
@@ -129,20 +142,20 @@ describe("Filters", () => {
|
||||
|
||||
it("should combine search terms", () => {
|
||||
const groups = [[{search: "hello"}], [{search: "world"}]]
|
||||
const result = Filters.intersectFilters(groups)
|
||||
const result = intersectFilters(groups)
|
||||
expect(result[0].search).toBe("hello world")
|
||||
})
|
||||
})
|
||||
|
||||
describe("getIdFilters", () => {
|
||||
it("should handle plain IDs", () => {
|
||||
const result = Filters.getIdFilters([id])
|
||||
const result = getIdFilters([id])
|
||||
expect(result[0].ids).toContain(id)
|
||||
})
|
||||
|
||||
it("should handle addresses", () => {
|
||||
const addr = `1:${pubkey}:test`
|
||||
const result = Filters.getIdFilters([addr])
|
||||
const result = getIdFilters([addr])
|
||||
expect(result[0]).toMatchObject({
|
||||
kinds: [1],
|
||||
authors: [pubkey],
|
||||
@@ -152,7 +165,7 @@ describe("Filters", () => {
|
||||
|
||||
it("should handle mixed IDs and addresses", () => {
|
||||
const addr = `1:${pubkey}:test`
|
||||
const result = Filters.getIdFilters([id, addr])
|
||||
const result = getIdFilters([id, addr])
|
||||
expect(result).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
@@ -160,13 +173,13 @@ describe("Filters", () => {
|
||||
describe("getReplyFilters", () => {
|
||||
it("should create filters for regular events", () => {
|
||||
const event = createEvent()
|
||||
const result = Filters.getReplyFilters([event])
|
||||
const result = getReplyFilters([event])
|
||||
expect((result[0] as any)["#e"]).toContain(event.id)
|
||||
})
|
||||
|
||||
it("should handle replaceable events", () => {
|
||||
const event = createEvent({kind: MUTES})
|
||||
const result = Filters.getReplyFilters([event])
|
||||
const result = getReplyFilters([event])
|
||||
expect((result[0] as any)["#a"]).toBeDefined()
|
||||
})
|
||||
|
||||
@@ -174,20 +187,20 @@ describe("Filters", () => {
|
||||
const event = createEvent({
|
||||
wrap: createEvent(),
|
||||
})
|
||||
const result = Filters.getReplyFilters([event])
|
||||
const result = getReplyFilters([event])
|
||||
expect((result[0] as any)["#e"]).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("addRepostFilters", () => {
|
||||
it("should add repost kinds for kind 1", () => {
|
||||
const result = Filters.addRepostFilters([{kinds: [1]}])
|
||||
const result = addRepostFilters([{kinds: [1]}])
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result[1].kinds).toContain(REPOST)
|
||||
})
|
||||
|
||||
it("should handle other kinds", () => {
|
||||
const result = Filters.addRepostFilters([{kinds: [LONG_FORM]}])
|
||||
const result = addRepostFilters([{kinds: [LONG_FORM]}])
|
||||
expect(result[1].kinds).toContain(GENERIC_REPOST)
|
||||
expect(result[1].kinds).not.toContain(REPOST)
|
||||
expect(result[1]["#k"]).toContain(LONG_FORM.toString())
|
||||
@@ -196,27 +209,27 @@ describe("Filters", () => {
|
||||
|
||||
describe("filter utilities", () => {
|
||||
it("should calculate filter generality", () => {
|
||||
expect(Filters.getFilterGenerality({ids: [id]})).toBe(0)
|
||||
expect(Filters.getFilterGenerality({authors: [pubkey], "#p": [pubkey]})).toBe(0.2)
|
||||
expect(Filters.getFilterGenerality({authors: [pubkey, pubkey, pubkey], kinds: [1]})).toBe(
|
||||
expect(getFilterGenerality({ids: [id]})).toBe(0)
|
||||
expect(getFilterGenerality({authors: [pubkey], "#p": [pubkey]})).toBe(0.2)
|
||||
expect(getFilterGenerality({authors: [pubkey, pubkey, pubkey], kinds: [1]})).toBe(
|
||||
0.01,
|
||||
)
|
||||
expect(Filters.getFilterGenerality({kinds: [1]})).toBe(1)
|
||||
expect(getFilterGenerality({kinds: [1]})).toBe(1)
|
||||
})
|
||||
|
||||
it("should guess filter delta", () => {
|
||||
const result = Filters.guessFilterDelta([{ids: [id]}])
|
||||
const result = guessFilterDelta([{ids: [id]}])
|
||||
expect(result).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it("should get filter result cardinality", () => {
|
||||
expect(Filters.getFilterResultCardinality({ids: [id, id + "1"]})).toBe(2)
|
||||
expect(Filters.getFilterResultCardinality({kinds: [1]})).toBeUndefined()
|
||||
expect(getFilterResultCardinality({ids: [id, id + "1"]})).toBe(2)
|
||||
expect(getFilterResultCardinality({kinds: [1]})).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should trim large filters", () => {
|
||||
const largeFilter = {authors: Array(2000).fill(pubkey)}
|
||||
const result = Filters.trimFilter(largeFilter)
|
||||
const result = trimFilter(largeFilter)
|
||||
expect(result.authors?.length).toBe(1000)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user