| author | Eric Roberts
<eric@devl.me> 2026-08-26 20:00:10 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-08-26 20:02:12 UTC |
| parent | 074248bbc0c8852bcc9497e7cee021210b650f04 |
| borogove/persistence/Sqlite.hx | +3 | -1 |
| test/TestSqlite.hx | +27 | -0 |
| test/idb.spec.ts | +0 | -80 |
| test/persistence-tests.ts | +94 | -0 |
| test/sqlite.spec.ts | +0 | -83 |
diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx index 73463e6..1b485ee 100644 --- a/borogove/persistence/Sqlite.hx +++ b/borogove/persistence/Sqlite.hx @@ -674,7 +674,9 @@ class Sqlite implements Persistence implements KeyValueStore { (SELECT MAX(CASE value->>'$.id' WHEN 'owner' THEN 4 WHEN 'admin' THEN 3 WHEN 'none' THEN 1 WHEN 'outcast' THEN 0 ELSE 2 END) FROM json_each(roles)), 2 ) AS role_rank, - CASE WHEN json(presence) NOT LIKE '% type=\\\"unavailable\\\"%' THEN 1 ELSE 0 END AS is_online + CASE WHEN EXISTS (SELECT 1 FROM json_each(presence)) + AND json(presence) NOT LIKE '% type=\\\"unavailable\\\"%' + THEN 1 ELSE 0 END AS is_online FROM members WHERE account_id=? AND chat_id=? diff --git a/test/TestSqlite.hx b/test/TestSqlite.hx index 44b23d2..06a4742 100644 --- a/test/TestSqlite.hx +++ b/test/TestSqlite.hx @@ -1212,6 +1212,33 @@ class TestSqlite extends utest.Test { }); } + public function testGetMembersTreatsEmptyPresenceAsOffline(async: Async) { + final account = "alice@example.com"; + final chat = new Channel(cast null, cast null, persistence, "room-members-empty-presence@example.com"); + chat.displayName = "A Chat"; + + persistence.storeMembers(account, chat.chatId, [ + new Member( + "room-members-empty-presence@example.com/guest", + "Guest", + null, + false, + [new Role("none", "Guest")], + JID.parse("guest@example.com"), + new Map(), + new AvailableChat("guest@example.com", "Guest", "", new borogove.Caps("", [], [], [])) + ) + ]).then(_ -> + persistence.getMembers(account, chat, false) + ).then(result -> { + Assert.equals(0, result.length); + async.done(); + }).catchError(e -> { + Assert.fail(Std.string(e)); + async.done(); + }); + } + public function testGetMembersIncludesModeratorVisibleRows(async: Async) { final account = "alice@example.com"; final chat = new Channel(cast null, cast null, persistence, "room-members-6@example.com"); diff --git a/test/idb.spec.ts b/test/idb.spec.ts index ee2e55c..2155f45 100644 --- a/test/idb.spec.ts +++ b/test/idb.spec.ts @@ -57,86 +57,6 @@ test("hydrate message with incomplete replyToMessage", async ({ expect(result).toBe("Hello"); }); -test("getMembers filters hidden rows for non-moderators", async ({ - page, - borogove, - persistence, -}) => { - const result = await page.evaluate( - async ({ borogove, persistence }) => { - const chat = Object.create(borogove.Channel.prototype); - chat.chatId = "room-members-5@example.com"; - chat.getDisplayName = () => "Tea Room"; - - await persistence.storeMembers("alice@example.com", chat.chatId, [ - { - id: "room-members-5@example.com/owner", - displayName: "Zulu", - photoUri: null, - isSelf: false, - roles: [{ id: "owner", title: "Owner" }], - jid: borogove.JID.parse("zulu@example.com"), - presence: new Map([["desk", borogove.Stanza.parse("<presence />")]]), - chat: { chatId: "zulu@example.com" }, - }, - { - id: "room-members-5@example.com/outcast", - displayName: "Banned", - photoUri: null, - isSelf: false, - roles: [{ id: "outcast", title: "Banned" }], - jid: borogove.JID.parse("banned@example.com"), - presence: new Map([["desk", borogove.Stanza.parse("<presence />")]]), - chat: { chatId: "banned@example.com" }, - }, - { - id: "room-members-5@example.com/guest-offline", - displayName: "Guest", - photoUri: null, - isSelf: false, - roles: [{ id: "none", title: "Guest" }], - jid: borogove.JID.parse("guest@example.com"), - presence: new Map([ - ["desk", borogove.Stanza.parse('<presence type="unavailable" />')], - ]), - chat: { chatId: "guest@example.com" }, - }, - { - id: "room-members-5@example.com/guest-offline2", - displayName: "Guest2", - photoUri: null, - isSelf: false, - roles: [{ id: "none", title: "Guest" }], - jid: borogove.JID.parse("guest2@example.com"), - presence: new Map(), - chat: { chatId: "guest2@example.com" }, - }, - { - id: "room-members-5@example.com/admin", - displayName: "Alpha", - photoUri: null, - isSelf: false, - roles: [{ id: "admin", title: "Admin" }], - jid: borogove.JID.parse("alpha@example.com"), - presence: new Map([["desk", borogove.Stanza.parse("<presence />")]]), - chat: { chatId: "alpha@example.com" }, - }, - ]); - - const normal = await persistence.getMembers( - "alice@example.com", - chat, - false, - ); - - return normal.map((m) => m.displayName); - }, - { borogove, persistence }, - ); - - expect(result).toEqual(["Zulu", "Alpha"]); -}); - test("hydrate message with incomplete replyToMessage keys", async ({ page, borogove, diff --git a/test/persistence-tests.ts b/test/persistence-tests.ts index eee2ea3..f198e79 100644 --- a/test/persistence-tests.ts +++ b/test/persistence-tests.ts @@ -1464,6 +1464,100 @@ export function sharedPersistenceTests(test: PersistenceTest) { expect(result.chat2PresenceKeys).toEqual(["phone"]); }); + test("getMembers filters hidden rows for non-moderators", async ({ + page, + borogove, + persistence, + }) => { + const result = await page.evaluate( + async ({ borogove, persistence }) => { + const chat = new borogove.Channel( + null, + null, + persistence, + "room-members-5@example.com", + ); + chat.displayName = "Tea Room"; + chat.trusted = true; + + await persistence.storeMembers("alice@example.com", chat.chatId, [ + { + id: "room-members-5@example.com/owner", + displayName: "Zulu", + photoUri: null, + isSelf: false, + roles: [{ id: "owner", title: "Owner" }], + jid: borogove.JID.parse("zulu@example.com"), + presence: new Map([ + ["desk", borogove.Stanza.parse("<presence />")], + ]), + chat: { chatId: "zulu@example.com" }, + }, + { + id: "room-members-5@example.com/outcast", + displayName: "Banned", + photoUri: null, + isSelf: false, + roles: [{ id: "outcast", title: "Banned" }], + jid: borogove.JID.parse("banned@example.com"), + presence: new Map([ + ["desk", borogove.Stanza.parse("<presence />")], + ]), + chat: { chatId: "banned@example.com" }, + }, + { + id: "room-members-5@example.com/guest-offline", + displayName: "Guest", + photoUri: null, + isSelf: false, + roles: [{ id: "none", title: "Guest" }], + jid: borogove.JID.parse("guest@example.com"), + presence: new Map([ + [ + "desk", + borogove.Stanza.parse('<presence type="unavailable" />'), + ], + ]), + chat: { chatId: "guest@example.com" }, + }, + { + id: "room-members-5@example.com/guest-offline2", + displayName: "Guest2", + photoUri: null, + isSelf: false, + roles: [{ id: "none", title: "Guest" }], + jid: borogove.JID.parse("guest2@example.com"), + presence: new Map(), + chat: { chatId: "guest2@example.com" }, + }, + { + id: "room-members-5@example.com/admin", + displayName: "Alpha", + photoUri: null, + isSelf: false, + roles: [{ id: "admin", title: "Admin" }], + jid: borogove.JID.parse("alpha@example.com"), + presence: new Map([ + ["desk", borogove.Stanza.parse("<presence />")], + ]), + chat: { chatId: "alpha@example.com" }, + }, + ]); + + const normal = await persistence.getMembers( + "alice@example.com", + chat, + false, + ); + + return normal.map((m) => m.displayName); + }, + { borogove, persistence }, + ); + + expect(result).toEqual(["Zulu", "Alpha"]); + }); + test("getMembers includes moderator-visible rows", async ({ page, borogove, diff --git a/test/sqlite.spec.ts b/test/sqlite.spec.ts index d60e4a5..299e75f 100644 --- a/test/sqlite.spec.ts +++ b/test/sqlite.spec.ts @@ -8,87 +8,4 @@ test.describe("not webkit", () => { ); sharedPersistenceTests(test); - - test("getMembers filters hidden rows for non-moderators", async ({ - page, - borogove, - persistence, - }) => { - const result = await page.evaluate( - async ({ borogove, persistence }) => { - const chat = new borogove.Channel( - null, - null, - persistence, - "room-members-5@example.com", - ); - chat.displayName = "A Chat"; - chat.trusted = true; - - await persistence.storeMembers("alice@example.com", chat.chatId, [ - { - id: "room-members-5@example.com/owner", - displayName: "Zulu", - photoUri: null, - isSelf: false, - roles: [{ id: "owner", title: "Owner" }], - jid: borogove.JID.parse("zulu@example.com"), - presence: new Map([ - ["desk", borogove.Stanza.parse("<presence />")], - ]), - chat: { chatId: "zulu@example.com" }, - }, - { - id: "room-members-5@example.com/outcast", - displayName: "Banned", - photoUri: null, - isSelf: false, - roles: [{ id: "outcast", title: "Banned" }], - jid: borogove.JID.parse("banned@example.com"), - presence: new Map([ - ["desk", borogove.Stanza.parse("<presence />")], - ]), - chat: { chatId: "banned@example.com" }, - }, - { - id: "room-members-5@example.com/guest-offline", - displayName: "Guest", - photoUri: null, - isSelf: false, - roles: [{ id: "none", title: "Guest" }], - jid: borogove.JID.parse("guest@example.com"), - presence: new Map([ - [ - "desk", - borogove.Stanza.parse('<presence type="unavailable" />'), - ], - ]), - chat: { chatId: "guest@example.com" }, - }, - { - id: "room-members-5@example.com/admin", - displayName: "Alpha", - photoUri: null, - isSelf: false, - roles: [{ id: "admin", title: "Admin" }], - jid: borogove.JID.parse("alpha@example.com"), - presence: new Map([ - ["desk", borogove.Stanza.parse("<presence />")], - ]), - chat: { chatId: "alpha@example.com" }, - }, - ]); - - const members = await persistence.getMembers( - "alice@example.com", - chat, - false, - ); - return members.map((m) => m.displayName); - }, - { borogove, persistence }, - ); - - expect(result).toEqual(["Zulu", "Alpha"]); - }); });