fix(chat): the review's gaps — owner hand-over, live group lines, names, scroll, one tab's sound, uploads on disk, a chat button without the pet
- A group whose creator left or was deleted passes to its longest-standing member (ensureOwner), so it can still be managed. - Group changes (added / removed / renamed / left) are sent live as chat_message and never count as unread, here or on the server. - A disabled account is still named in old chats (people carries `disabled`) and never offered for a new one; a deleted one reads 'Deleted user' instead of '?'. - Load older keeps the message being read in place. - With several tabs open only the first to see a message plays its sound / system notification (agenthub.chat.notified). - The people list is read again on New chat and on every room change. - Uploads go to <data dir>/chat/.incoming/ (multer disk storage) and are renamed into place — never held in memory; a refused one is removed. - With the pet hidden, a chat button in the bottom-right corner (ChatDock, server mode) opens the same window, unread count and new-message card included.
This commit is contained in:
@@ -81,6 +81,7 @@ describe("chat", () => {
|
||||
it("lists the other enabled users to chat with", async () => {
|
||||
const r = await call(2, "GET", "/people");
|
||||
assert.deepEqual(r.body.people.map((p) => p.username).sort(), ["binh", "boss", "chi"]);
|
||||
assert.ok(r.body.people.every((p) => p.disabled === false));
|
||||
});
|
||||
|
||||
it("opens one direct chat per pair, whoever starts it", async () => {
|
||||
@@ -250,6 +251,40 @@ describe("chat", () => {
|
||||
assert.deepEqual([...scope.exactAudience("chat_read", { to: [2, 3] })], [2, 3]);
|
||||
});
|
||||
|
||||
it("hands a group to its longest-standing member when the creator leaves", async () => {
|
||||
const g = (await call(2, "POST", "/rooms", { kind: "group", name: "Kế", userIds: [3, 4] })).body
|
||||
.room.id;
|
||||
await call(2, "POST", `/rooms/${g}/leave`);
|
||||
const room = (await call(3, "GET", "/rooms")).body.rooms.find((r) => r.id === g);
|
||||
assert.equal(room.createdBy, 3);
|
||||
assert.equal((await call(3, "PATCH", `/rooms/${g}`, { name: "Kế 2" })).status, 200);
|
||||
// The "left" and "renamed" lines never count as unread.
|
||||
const four = (await call(4, "GET", "/rooms")).body.rooms.find((r) => r.id === g);
|
||||
assert.equal(four.unread, 0);
|
||||
});
|
||||
|
||||
it("still names a disabled user, and never offers them for a new chat", async () => {
|
||||
const { db } = require("../db");
|
||||
db.prepare("UPDATE users SET disabled = 1 WHERE id = 4").run();
|
||||
try {
|
||||
const chi = (await call(2, "GET", "/people")).body.people.find((p) => p.id === 4);
|
||||
assert.equal(chi.disabled, true);
|
||||
assert.equal((await call(2, "POST", "/rooms", { kind: "dm", userId: 4 })).status, 400);
|
||||
} finally {
|
||||
db.prepare("UPDATE users SET disabled = 0 WHERE id = 4").run();
|
||||
}
|
||||
});
|
||||
|
||||
it("leaves nothing behind in the upload folder", async () => {
|
||||
const fd = new FormData();
|
||||
fd.append("files", new Blob(["z"], { type: "text/plain" }), "d.txt");
|
||||
assert.equal((await call(2, "POST", `/rooms/${group}/messages`, fd)).status, 201);
|
||||
const fd2 = new FormData();
|
||||
fd2.append("files", new Blob(["z"], { type: "text/plain" }), "e.txt");
|
||||
assert.equal((await call(1, "POST", `/rooms/${group}/messages`, fd2)).status, 404);
|
||||
assert.deepEqual(fs.readdirSync(path.join(TMP, "chat", ".incoming")), []);
|
||||
});
|
||||
|
||||
it("refuses an empty message", async () => {
|
||||
assert.equal((await call(2, "POST", `/rooms/${dm}/messages`, { body: " " })).status, 400);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user