borogove

created pr with borogove/call-fixes.1 on 2026-11-03T21:19:14Z · by 9e834e0d
added borogove/call-fixes.2 on 2026-11-09T14:58:54Z · by 9e834e0d
1: dbd8243 ! 1: a3fc6ef Never write ring before propose
2: 5ebad9d = 2: 5888d2b Presetve stanza ID for call updates
3: 9d8f8cc = 3: fc1c6dd Get all versions of a call from sqlite
commented on 2026-11-09T15:01:23Z · by 3c31bc8c
lgtm
cmds
checkout latest patchset:
ssh pr.pico.sh pull borogove:call-fixes | git am -3
checkout specific patchset revision:
ssh pr.pico.sh pull borogove:call-fixes [rev] | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh borogove:call-fixes
add comment to patch request:
ssh pr.pico.sh comment borogove:call-fixes "lgtm!"
+1 -1 borogove/Message.hx #
......@@ -252,7 +252,7 @@ class Message {
252252 final version = msg.build();
253253 // The session id is what really identifies us
254254 msg.localId = jmi.attr.get("id");
255- if (jmi.name != "propose") {
255+ if (jmi.name != "propose" && msg.versions.length < 1) {
256256 msg.versions = [version];
257257 }
258258 }
+22 -9 borogove/persistence/Sqlite.hx #
......@@ -876,17 +876,20 @@ class Sqlite implements Persistence implements KeyValueStore {
876876 SELECT
877877 $versionedMessageColumns
878878 FROM messages
879- INNER JOIN messages versions USING (correction_id, sender_id)
879+ $versionedMessagesJoin
880880 WHERE
881881 messages.account_id=?
882882 AND messages.chat_id=?
883- AND (messages.stanza_id IS NULL OR messages.stanza_id=\'\' OR messages.stanza_id=correction_id)
883+ AND (messages.stanza_id IS NULL OR messages.stanza_id=\'\' OR messages.stanza_id=messages.correction_id)
884884 ';
885- var params = [accountId, stub.chatId()];
885+ var params:Array<Dynamic> = [MessageCall, accountId, stub.chatId()];
886886 if (stub.versions.length > 0 || stub.serverId == null) {
887- q += " AND correction_id=? AND sender_id=?";
887+ q += " AND messages.correction_id=?";
888888 params.push(stub.localId);
889- params.push(stub.senderId);
889+ if (stub.type != MessageCall) {
890+ q += " AND messages.sender_id=?";
891+ params.push(stub.senderId);
892+ }
890893 } else if (stub.serverId != null) {
891894 q += " AND messages.mam_id=? AND messages.mam_by=?";
892895 params.push(stub.serverId);
......@@ -894,7 +897,8 @@ class Sqlite implements Persistence implements KeyValueStore {
894897 } else {
895898 throw "No way to look up this message";
896899 }
897- q += " GROUP BY correction_id, CASE WHEN messages.type=? THEN 'call' ELSE messages.sender_id END";
900+ q += " GROUP BY messages.correction_id, CASE WHEN messages.type=? THEN 'call' ELSE messages.sender_id END";
901+ params.push(MessageCall);
898902 return db.exec(q, params).then(results -> {
899903 final row = results.next();
900904 if (row == null) return stub;
......@@ -952,18 +956,19 @@ class Sqlite implements Persistence implements KeyValueStore {
952956 SELECT
953957 $versionedMessageColumns
954958 FROM messages
955- INNER JOIN messages versions USING (correction_id, sender_id)
959+ $versionedMessagesJoin
956960 WHERE
957961 (messages.stanza_id, messages.mam_id) IN (SELECT * FROM page)
958962 AND messages.account_id=?
959963 AND messages.chat_id=?
960- GROUP BY correction_id, CASE WHEN messages.type=? THEN \'call\' ELSE messages.sender_id END
964+ GROUP BY messages.correction_id, CASE WHEN messages.type=? THEN \'call\' ELSE messages.sender_id END
961965 ';
962966 q += " ORDER BY messages.sort_id";
963967 if (op == "<" || op == "<=") q += " DESC";
964968 q += ", messages.time_received";
965969 if (op == "<" || op == "<=") q += " DESC";
966970
971+ params.push(MessageCall);
967972 params.push(accountId);
968973 params.push(chatId);
969974 params.push(MessageCall);
......@@ -1848,8 +1853,16 @@ class Sqlite implements Persistence implements KeyValueStore {
18481853 .map(column -> column.sql);
18491854 }
18501855
1856+ private final versionedMessagesJoin = "
1857+ INNER JOIN messages versions
1858+ ON versions.correction_id = messages.correction_id
1859+ AND CASE
1860+ WHEN messages.type = ? THEN versions.type = messages.type
1861+ ELSE versions.sender_id = messages.sender_id
1862+ END";
1863+
18511864 private final versionedMessageColumns = "
1852- correction_id AS stanza_id,
1865+ messages.correction_id AS stanza_id,
18531866 json_group_object(
18541867 CASE
18551868 WHEN versions.mam_id IS NULL OR versions.mam_id=''
+134 -43 test/persistence-tests.ts #
......@@ -1,6 +1,57 @@
11 import { faker } from "@faker-js/faker";
22 import { expect } from "vitest";
33
4+function createCallMessage(
5+ borogove: any,
6+ account: string,
7+ contact: string,
8+ sid: string,
9+) {
10+ return ({
11+ direction,
12+ event,
13+ localId = sid,
14+ serverId = null,
15+ sortId,
16+ timeReceived,
17+ versions = [],
18+ }: {
19+ direction: number;
20+ event: string;
21+ localId?: string;
22+ serverId?: string | null;
23+ sortId: string;
24+ timeReceived: string;
25+ versions?: any[];
26+ }) => {
27+ const received = direction === borogove.MessageDirection.MessageReceived;
28+ const from = received ? contact : account;
29+ const to = received ? account : contact;
30+ const builder = new borogove.ChatMessageBuilder({
31+ localId,
32+ serverId,
33+ serverIdBy: serverId == null ? null : account,
34+ senderId: from,
35+ direction,
36+ type: borogove.MessageType.MessageCall,
37+ timeReceived,
38+ versions,
39+ payloads: [
40+ borogove.Stanza.parse(
41+ `<${event} xmlns="urn:xmpp:jingle-message:0" id="${sid}"/>`,
42+ ),
43+ ],
44+ text: "call " + event,
45+ });
46+ builder.sortId = sortId;
47+ builder.to = borogove.JID.parse(to);
48+ builder.from = borogove.JID.parse(from);
49+ builder.recipients = [builder.to];
50+ builder.replyTo = [builder.from];
51+ return builder.build();
52+ };
53+}
54+
455 export function sharedPersistenceTests(test) {
556 test("getMessagesByStatus filters and orders messages", async ({
657 borogove,
......@@ -2382,49 +2433,7 @@ export function sharedPersistenceTests(test) {
23822433 const sid = "ordering-outgoing-call";
23832434 const proposedAt = "2024-01-01T00:00:00.000Z";
23842435 const rejectedAt = "2024-01-01T00:00:01.000Z";
2385- const callMessage = ({
2386- direction,
2387- event,
2388- localId,
2389- sortId,
2390- timeReceived,
2391- serverId,
2392- versions,
2393- }: {
2394- direction: number;
2395- event: string;
2396- localId?: string;
2397- sortId: string;
2398- timeReceived: string;
2399- serverId?: string;
2400- versions?: any[];
2401- }) => {
2402- const received = direction === borogove.MessageDirection.MessageReceived;
2403- const from = received ? contact : account;
2404- const to = received ? account : contact;
2405- const builder = new borogove.ChatMessageBuilder({
2406- localId: localId ?? sid,
2407- serverId: serverId ?? null,
2408- serverIdBy: serverId == null ? null : account,
2409- senderId: from,
2410- direction,
2411- type: borogove.MessageType.MessageCall,
2412- timeReceived,
2413- versions: versions ?? [],
2414- payloads: [
2415- borogove.Stanza.parse(
2416- `<${event} xmlns="urn:xmpp:jingle-message:0" id="${sid}"/>`,
2417- ),
2418- ],
2419- text: "call " + event,
2420- });
2421- builder.sortId = sortId;
2422- builder.to = borogove.JID.parse(to);
2423- builder.from = borogove.JID.parse(from);
2424- builder.recipients = [builder.to];
2425- builder.replyTo = [builder.from];
2426- return builder.build();
2427- };
2436+ const callMessage = createCallMessage(borogove, account, contact, sid);
24282437
24292438 const propose = callMessage({
24302439 direction: borogove.MessageDirection.MessageSent,
......@@ -2463,6 +2472,88 @@ export function sharedPersistenceTests(test) {
24632472 borogove.MessageDirection.MessageSent,
24642473 );
24652474 });
2475+
2476+ test("getMessagesBefore includes the full call log across directions", async ({
2477+ borogove,
2478+ persistence,
2479+ }) => {
2480+ const account = "call-log-alice@example.com";
2481+ const contact = "call-log-hatter@example.com";
2482+ const sid = "call-log-sid";
2483+ const callMessage = createCallMessage(borogove, account, contact, sid);
2484+ const callUpdate = ({
2485+ direction,
2486+ event,
2487+ sortId,
2488+ timeReceived,
2489+ }: {
2490+ direction: number;
2491+ event: string;
2492+ sortId: string;
2493+ timeReceived: string;
2494+ }) => {
2495+ const serverId = `call-log-${event}-server-id`;
2496+ const version = callMessage({
2497+ direction,
2498+ event,
2499+ localId: `call-log-${event}-stanza-id`,
2500+ serverId,
2501+ sortId,
2502+ timeReceived,
2503+ });
2504+ return callMessage({
2505+ direction,
2506+ event,
2507+ serverId,
2508+ sortId,
2509+ timeReceived,
2510+ versions: [version],
2511+ });
2512+ };
2513+
2514+ await persistence.storeMessages(account, [
2515+ callMessage({
2516+ direction: borogove.MessageDirection.MessageSent,
2517+ event: "propose",
2518+ serverId: "call-log-propose-server-id",
2519+ sortId: "call-log-a0",
2520+ timeReceived: "2024-02-01T00:00:00.000Z",
2521+ }),
2522+ ]);
2523+ for (const update of [
2524+ {
2525+ direction: borogove.MessageDirection.MessageReceived,
2526+ event: "ringing",
2527+ sortId: "call-log-a1",
2528+ timeReceived: "2024-02-01T00:00:01.000Z",
2529+ },
2530+ {
2531+ direction: borogove.MessageDirection.MessageReceived,
2532+ event: "proceed",
2533+ sortId: "call-log-a2",
2534+ timeReceived: "2024-02-01T00:00:02.000Z",
2535+ },
2536+ {
2537+ direction: borogove.MessageDirection.MessageSent,
2538+ event: "finish",
2539+ sortId: "call-log-a3",
2540+ timeReceived: "2024-02-01T00:00:03.000Z",
2541+ },
2542+ ]) {
2543+ await persistence.storeMessages(account, [callUpdate(update)]);
2544+ }
2545+
2546+ const messages = await persistence.getMessagesBefore(account, contact);
2547+ const call = messages.find((message) => message.localId === sid);
2548+ expect(call).toBeDefined();
2549+ expect(call?.type).toBe(borogove.MessageType.MessageCall);
2550+ expect(call?.versions.map((version) => version.callStatus())).toEqual([
2551+ "finish",
2552+ "proceed",
2553+ "ringing",
2554+ "propose",
2555+ ]);
2556+ });
24662557 }
24672558
24682559 type TestKeyPair = {
Back to top