fix: harden identity, live access, drafts and setup (review only)
Source-reviewed hardening changes and regression tests. UNBUILT / UNTESTED: not a deployable release. Go formatting, compilation, automated regression tests, full frontend validation and staging acceptance remain required. See docs/hardening/VALIDATION.md. Main remains the original clean snapshot; no upstream Git history imported.
This commit is contained in:
99
web/lib/message-editor-drafts.test.mjs
Normal file
99
web/lib/message-editor-drafts.test.mjs
Normal file
@@ -0,0 +1,99 @@
|
||||
import assert from "node:assert/strict"
|
||||
import test from "node:test"
|
||||
|
||||
import { createMessageEditorDraftStore } from "./message-editor-drafts.ts"
|
||||
|
||||
test("conversation drafts stay separate across A → B → A", () => {
|
||||
const store = createMessageEditorDraftStore()
|
||||
const a = store.get("1")
|
||||
store.update(a, "<p>For A</p>")
|
||||
const b = store.get("2")
|
||||
assert.equal(b.html, "")
|
||||
store.update(b, "<p>For B</p>")
|
||||
assert.equal(store.get("1").html, "<p>For A</p>")
|
||||
assert.equal(store.get("2").html, "<p>For B</p>")
|
||||
})
|
||||
|
||||
test("failed send preserves text and uploaded image metadata, and can retry", () => {
|
||||
const store = createMessageEditorDraftStore()
|
||||
const draft = store.get("1")
|
||||
store.update(draft, '<p>Message</p><img title="uploading-test">')
|
||||
draft.uploadedImages.set("uploading-test", { assetId: "test", provider: "local", storageKey: "test", url: "/test" })
|
||||
assert.equal(store.finishSend(store.beginSend("1"), false), false)
|
||||
assert.equal(draft.html, '<p>Message</p><img title="uploading-test">')
|
||||
assert.equal(draft.uploadedImages.size, 1)
|
||||
assert.ok(store.beginSend("1"))
|
||||
})
|
||||
|
||||
test("send is single-flight, success clears only the captured draft", () => {
|
||||
const store = createMessageEditorDraftStore()
|
||||
const a = store.get("1")
|
||||
const b = store.get("2")
|
||||
store.update(a, "For A")
|
||||
store.update(b, "For B")
|
||||
const sent = store.beginSend("1")
|
||||
assert.equal(store.beginSend("1"), null)
|
||||
assert.equal(store.beginSend("2"), null)
|
||||
assert.equal(store.finishSend(sent, true), true)
|
||||
assert.equal(a.html, "")
|
||||
assert.equal(b.html, "For B")
|
||||
assert.ok(store.beginSend("2"))
|
||||
})
|
||||
|
||||
test("typing during a pending send survives success, even after returning to identical text", () => {
|
||||
const store = createMessageEditorDraftStore()
|
||||
const draft = store.get("1")
|
||||
store.update(draft, "Initial")
|
||||
const sent = store.beginSend("1")
|
||||
store.update(draft, "Next draft")
|
||||
assert.equal(store.finishSend(sent, true), false)
|
||||
assert.equal(draft.html, "Next draft")
|
||||
const nextSend = store.beginSend("1")
|
||||
store.update(draft, "Changed")
|
||||
store.update(draft, "Next draft")
|
||||
assert.equal(store.finishSend(nextSend, true), false)
|
||||
assert.equal(draft.html, "Next draft")
|
||||
})
|
||||
|
||||
test("identity change clears private drafts and invalidates old completions", () => {
|
||||
const store = createMessageEditorDraftStore()
|
||||
store.setScope("account-a")
|
||||
const previous = store.get("1")
|
||||
store.update(previous, "Private")
|
||||
const sent = store.beginSend("1")
|
||||
store.setScope("account-b")
|
||||
const next = store.get("1")
|
||||
store.update(next, "New account")
|
||||
const nextSent = store.beginSend("1")
|
||||
assert.equal(store.contains("1", previous), false)
|
||||
assert.equal(store.finishSend(sent, true), false)
|
||||
assert.equal(next.html, "New account")
|
||||
assert.equal(store.finishSend(nextSent, true), true)
|
||||
})
|
||||
|
||||
test("a newly mounted editor observes completion from the previous editor instance", () => {
|
||||
const store = createMessageEditorDraftStore()
|
||||
const draft = store.get("1")
|
||||
store.update(draft, "Pending")
|
||||
const sent = store.beginSend("1")
|
||||
let rendered = draft.html
|
||||
const unsubscribe = store.subscribe((changed) => {
|
||||
if (changed === draft) rendered = changed.html
|
||||
})
|
||||
store.finishSend(sent, true)
|
||||
assert.equal(rendered, "")
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
test("pending status stays observable when switching or remounting editors", () => {
|
||||
const store = createMessageEditorDraftStore()
|
||||
store.get("1")
|
||||
const states = []
|
||||
const unsubscribe = store.subscribeSending(() => states.push(store.isSending()))
|
||||
const sent = store.beginSend("1")
|
||||
store.get("2")
|
||||
assert.equal(store.isSending(), true)
|
||||
store.finishSend(sent, false)
|
||||
assert.deepEqual(states, [true, false])
|
||||
unsubscribe()
|
||||
})
|
||||
Reference in New Issue
Block a user