Add domain package
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import {describe, it, expect} from "vitest"
|
||||
import {makeSecret, MUTES, FOLLOWS, getPubkeyTagValues} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {Nip01Signer} from "@welshman/signer"
|
||||
import {MuteList} from "../src/MuteList"
|
||||
|
||||
const signer = new Nip01Signer(makeSecret())
|
||||
|
||||
const a = "aa".repeat(32)
|
||||
const b = "bb".repeat(32)
|
||||
const c = "cc".repeat(32)
|
||||
|
||||
describe("MuteList", () => {
|
||||
it("round-trips public and private mutes through encryption", async () => {
|
||||
const list = MuteList.make().addPublicly(a).addPrivately(b)
|
||||
|
||||
expect(list.pubkeys.sort()).toEqual([a, b].sort())
|
||||
expect(list.includes(a)).toBe(true)
|
||||
expect(list.includes(b)).toBe(true)
|
||||
expect(list.includes(c)).toBe(false)
|
||||
|
||||
const event = await list.toEvent(signer)
|
||||
|
||||
expect(event.kind).toBe(MUTES)
|
||||
expect(event.sig).toBeTruthy()
|
||||
// Public entry is visible in tags; private entry is encrypted in content.
|
||||
expect(getPubkeyTagValues(event.tags)).toEqual([a])
|
||||
expect(event.content).not.toBe("")
|
||||
|
||||
// Re-parsing with a capable signer recovers the private entries.
|
||||
const decrypted = await MuteList.parse(event, signer)
|
||||
|
||||
expect(decrypted.isDecrypted).toBe(true)
|
||||
expect(decrypted.pubkeys.sort()).toEqual([a, b].sort())
|
||||
|
||||
// Parsing without a signer exposes only the public entries.
|
||||
const publicOnly = await MuteList.parse(event)
|
||||
|
||||
expect(publicOnly.isDecrypted).toBe(false)
|
||||
expect(publicOnly.pubkeys).toEqual([a])
|
||||
})
|
||||
|
||||
it("removes from both public and private entries", async () => {
|
||||
const list = MuteList.make().addPublicly(a).addPrivately(b)
|
||||
|
||||
list.remove(a)
|
||||
list.remove(b)
|
||||
|
||||
expect(list.pubkeys).toEqual([])
|
||||
})
|
||||
|
||||
it("preserves undecrypted ciphertext on pass-through serialization", async () => {
|
||||
const event = await MuteList.make().addPrivately(b).toEvent(signer)
|
||||
const undecrypted = await MuteList.parse(event)
|
||||
|
||||
// We never decrypted, so the original ciphertext must survive untouched.
|
||||
const template = await undecrypted.getTemplate(signer)
|
||||
|
||||
expect(template.content).toBe(event.content)
|
||||
})
|
||||
|
||||
it("refuses private mutation when undecrypted", async () => {
|
||||
const event = await MuteList.make().addPrivately(b).toEvent(signer)
|
||||
const undecrypted = await MuteList.parse(event)
|
||||
|
||||
expect(() => undecrypted.addPrivately(c)).toThrow()
|
||||
})
|
||||
|
||||
it("toRumor encrypts but does not sign", async () => {
|
||||
const rumor = await MuteList.make().addPrivately(b).toRumor(signer)
|
||||
|
||||
expect(rumor.id).toBeTruthy()
|
||||
expect((rumor as TrustedEvent).sig).toBeUndefined()
|
||||
expect(rumor.content).not.toBe("")
|
||||
})
|
||||
|
||||
it("serializes to JSON", async () => {
|
||||
const list = MuteList.make().addPublicly(a).addPrivately(b)
|
||||
const json = JSON.parse(JSON.stringify(list))
|
||||
|
||||
expect(json.kind).toBe(MUTES)
|
||||
expect(json.publicTags).toEqual([["p", a]])
|
||||
expect(json.privateTags).toEqual([["p", b]])
|
||||
})
|
||||
|
||||
it("throws on the wrong kind", async () => {
|
||||
const event = {kind: FOLLOWS, tags: [], content: "", pubkey: a} as TrustedEvent
|
||||
|
||||
await expect(MuteList.parse(event)).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,73 @@
|
||||
import {describe, it, expect} from "vitest"
|
||||
import {makeSecret, PROFILE, NOTE} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {Nip01Signer} from "@welshman/signer"
|
||||
import {Profile, displayPubkey} from "../src/Profile"
|
||||
|
||||
const signer = new Nip01Signer(makeSecret())
|
||||
const pubkey = "ee".repeat(32)
|
||||
|
||||
const makeEvent = (overrides: Partial<TrustedEvent> = {}): TrustedEvent =>
|
||||
({
|
||||
id: "ff".repeat(32),
|
||||
pubkey,
|
||||
created_at: 0,
|
||||
kind: PROFILE,
|
||||
tags: [],
|
||||
content: "",
|
||||
sig: "00".repeat(64),
|
||||
...overrides,
|
||||
}) as TrustedEvent
|
||||
|
||||
describe("Profile", () => {
|
||||
it("parses and re-signs profile content", async () => {
|
||||
const event = makeEvent({
|
||||
content: JSON.stringify({name: "alice", about: "hi"}),
|
||||
tags: [["alt", "profile"]],
|
||||
})
|
||||
|
||||
const profile = Profile.parse(event)
|
||||
|
||||
expect(profile.values.name).toBe("alice")
|
||||
expect(profile.hasName()).toBe(true)
|
||||
expect(profile.display()).toBe("alice")
|
||||
|
||||
const signed = await profile.toEvent(signer)
|
||||
|
||||
expect(signed.kind).toBe(PROFILE)
|
||||
expect(JSON.parse(signed.content).name).toBe("alice")
|
||||
// Source tags are preserved.
|
||||
expect(signed.tags).toEqual([["alt", "profile"]])
|
||||
})
|
||||
|
||||
it("derives lnurl from a lud16 address", () => {
|
||||
const profile = Profile.make({lud16: "alice@example.com"})
|
||||
|
||||
expect(profile.values.lnurl).toBeTruthy()
|
||||
})
|
||||
|
||||
it("set merges and re-derives values", () => {
|
||||
const profile = Profile.make({name: "alice"})
|
||||
|
||||
profile.set({about: "hello"})
|
||||
|
||||
expect(profile.values.name).toBe("alice")
|
||||
expect(profile.values.about).toBe("hello")
|
||||
})
|
||||
|
||||
it("display falls back to a shortened npub", () => {
|
||||
const profile = Profile.parse(makeEvent({content: "{}"}))
|
||||
|
||||
expect(profile.display()).toBe(displayPubkey(pubkey))
|
||||
})
|
||||
|
||||
it("serializes to JSON", () => {
|
||||
const profile = Profile.make({name: "alice"})
|
||||
|
||||
expect(JSON.parse(JSON.stringify(profile))).toEqual({name: "alice"})
|
||||
})
|
||||
|
||||
it("throws on the wrong kind", () => {
|
||||
expect(() => Profile.parse(makeEvent({kind: NOTE}))).toThrow()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user