mkstackwithwelshman/src/lib/Encryptable.test.ts
Patrick 9b81dce394
Some checks failed
Deploy to GitHub Pages / deploy (push) Has been cancelled
Test / test (push) Has been cancelled
feat: Add comprehensive @welshman/util v0.4.2 test coverage
- Add complete test suites for all major Welshman APIs:
  - Address: Addressable event handling (20 tests)
  - Encryptable: Encryption/decryption functionality (11 tests)
  - Filters: Query optimization and matching (26 tests)
  - Handler: NIP-89 application handlers (15 tests)
  - List: Public/private list management (24 tests)
  - Profile: User profile management (22 tests)
  - Tags: Tag parsing and validation (16 tests)
  - Zaps: Lightning Network integration (20 tests)
- Achieve 100% test coverage with 203/203 tests passing
- Update README to reflect comprehensive API coverage
- Validate complete Nostr development framework functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 07:10:40 -04:00

223 lines
6.3 KiB
TypeScript

import {MUTES} from "@welshman/util"
import {now} from "@welshman/lib"
import {describe, it, expect, vi, beforeEach} from "vitest"
import {Encryptable, asDecryptedEvent} from "@welshman/util"
import type {OwnedEvent, TrustedEvent} from "@welshman/util"
describe("Encryptable", () => {
beforeEach(() => {
vi.clearAllMocks()
})
// Mock encryption function
const mockEncrypt = vi.fn(async (text: string) => `encrypted:${text}`)
// Realistic Nostr values
const pub = "ee".repeat(32)
const currentTime = now()
describe("constructor", () => {
it("should create an instance with minimal event template", () => {
const event: Partial<OwnedEvent> = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
}
const encryptable = new Encryptable(event, {})
expect(encryptable.event).toBe(event)
expect(encryptable.updates).toEqual({})
})
it("should create an instance with full event template", () => {
const event: OwnedEvent = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
content: "original encrypted content",
tags: [["p", pub]],
}
const updates = {
content: JSON.stringify({list: ["item1", "item2"]}),
tags: [["p", pub, "wss://relay.example.com"]],
}
const encryptable = new Encryptable(event, updates)
expect(encryptable.event).toBe(event)
expect(encryptable.updates).toBe(updates)
})
})
describe("reconcile", () => {
it("should encrypt content updates", async () => {
const event: Partial<OwnedEvent> = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
}
const updates = {
content: JSON.stringify({muted: [pub]}),
}
const encryptable = new Encryptable(event, updates)
const result = await encryptable.reconcile(mockEncrypt)
expect(result.content).toBe(`encrypted:${updates.content}`)
expect(mockEncrypt).toHaveBeenCalledWith(updates.content)
})
it("should encrypt tag updates", async () => {
const event: Partial<OwnedEvent> = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
}
const updates = {
tags: [["p", pub, "wss://relay.example.com"]],
}
const encryptable = new Encryptable(event, updates)
const result = await encryptable.reconcile(mockEncrypt)
expect(result.tags[0][1]).toBe(`encrypted:${pub}`)
expect(mockEncrypt).toHaveBeenCalledWith(pub)
})
it("should handle both content and tag updates", async () => {
const event: Partial<OwnedEvent> = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
}
const updates = {
content: JSON.stringify({muted: [pub]}),
tags: [["p", pub, "wss://relay.example.com"]],
}
const encryptable = new Encryptable(event, updates)
const result = await encryptable.reconcile(mockEncrypt)
expect(result.content).toBe(`encrypted:${updates.content}`)
expect(result.tags[0][1]).toBe(`encrypted:${pub}`)
expect(mockEncrypt).toHaveBeenCalledTimes(2)
})
it("should preserve original content when no updates", async () => {
const event: OwnedEvent = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
content: JSON.stringify({originalList: [pub]}),
tags: [],
}
const encryptable = new Encryptable(event, {})
const result = await encryptable.reconcile(mockEncrypt)
expect(result.content).toBe(event.content)
expect(mockEncrypt).not.toHaveBeenCalled()
})
it("should preserve original tags when no updates", async () => {
const event: OwnedEvent = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
content: "",
tags: [["p", pub, "wss://relay.example.com"]],
}
const encryptable = new Encryptable(event, {})
const result = await encryptable.reconcile(mockEncrypt)
expect(result.tags).toEqual(event.tags)
expect(mockEncrypt).not.toHaveBeenCalled()
})
})
describe("asDecryptedEvent", () => {
it("should create a decrypted event with plaintext", () => {
const event: TrustedEvent = {
id: "ff".repeat(32),
sig: "00".repeat(64),
kind: MUTES,
pubkey: pub,
created_at: currentTime,
content: "encrypted content",
tags: [],
}
const plaintext = {
content: JSON.stringify({muted: [pub]}),
tags: [["p", pub, "wss://relay.example.com"]],
}
const result = asDecryptedEvent(event, plaintext)
expect(result).toEqual({
...event,
plaintext,
})
})
it("should handle empty plaintext", () => {
const event: TrustedEvent = {
id: "ff".repeat(32),
sig: "00".repeat(64),
kind: MUTES,
pubkey: pub,
created_at: currentTime,
content: "encrypted content",
tags: [],
}
const result = asDecryptedEvent(event)
expect(result).toEqual({
...event,
plaintext: {},
})
})
})
describe("error handling", () => {
it("should handle encryption failures", async () => {
const failingEncrypt = async () => {
throw new Error("Encryption failed")
}
const event: Partial<OwnedEvent> = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
}
const updates = {
content: JSON.stringify({muted: [pub]}),
}
const encryptable = new Encryptable(event, updates)
await expect(encryptable.reconcile(failingEncrypt)).rejects.toThrow("Encryption failed")
})
it("should handle partial encryption failures", async () => {
let callCount = 0
const partialFailingEncrypt = async () => {
callCount++
if (callCount > 1) throw new Error("Encryption failed")
return "encrypted:success"
}
const event: Partial<OwnedEvent> = {
kind: MUTES,
pubkey: pub,
created_at: currentTime,
}
const updates = {
content: JSON.stringify({muted: [pub]}),
tags: [["p", pub]],
}
const encryptable = new Encryptable(event, updates)
await expect(encryptable.reconcile(partialFailingEncrypt)).rejects.toThrow(
"Encryption failed",
)
})
})
})