| author | Eric Roberts
<eric@devl.me> 2026-08-26 18:14:48 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-08-26 18:16:17 UTC |
| parent | 959b45252e716145858b695c8a9c790e7c8cf491 |
| borogove/Persistence.hx | +1 | -1 |
| borogove/persistence/Dummy.hx | +3 | -1 |
| borogove/persistence/IDB.js | +5 | -3 |
| borogove/persistence/Sqlite.hx | +35 | -2 |
| test/TestSqlite.hx | +38 | -0 |
| test/persistence-tests.ts | +54 | -0 |
diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx index 03d7834..4ea7dec 100644 --- a/borogove/Persistence.hx +++ b/borogove/Persistence.hx @@ -365,7 +365,7 @@ interface Persistence { /** Store the known OMEMO device list for a contact or account **/ - public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Void; + public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Promise<Array<Int>>; /** Store an OMEMO pre-key diff --git a/borogove/persistence/Dummy.hx b/borogove/persistence/Dummy.hx index 46c56ab..bed3908 100644 --- a/borogove/persistence/Dummy.hx +++ b/borogove/persistence/Dummy.hx @@ -209,7 +209,9 @@ class Dummy implements Persistence { } @HaxeCBridge.noemit - public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Void { } + public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Promise<Array<Int>> { + return Promise.resolve(deviceIds); + } @HaxeCBridge.noemit public function storeOmemoPreKey(identifier:String, keyId:Int, keyPair:PreKeyPair):Void { } diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js index 07ae61b..90b3c62 100644 --- a/borogove/persistence/IDB.js +++ b/borogove/persistence/IDB.js @@ -1157,15 +1157,17 @@ tx.onerror = console.error; return keypair; }, - storeOmemoDeviceList(chatId, deviceIds) { + async storeOmemoDeviceList(chatId, deviceIds) { const tx = db.transaction(["keyvaluepairs"], "readwrite"); const store = tx.objectStore("keyvaluepairs"); const key = "omemo:devices:"+chatId; if(deviceIds.length>0) { - store.put(deviceIds, key); + await promisifyRequest(store.put(deviceIds, key)); } else { - store.delete(key); + await promisifyRequest(store.delete(key)); } + + return deviceIds; }, async getOmemoDeviceList(chatId) { diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx index 38807c0..5016c94 100644 --- a/borogove/persistence/Sqlite.hx +++ b/borogove/persistence/Sqlite.hx @@ -278,6 +278,16 @@ class Sqlite implements Persistence implements KeyValueStore { "PRAGMA user_version = 14"]); } return Promise.resolve(null); + }).then(_ -> { + if (version < 15) { + return exec(["CREATE TABLE omemo_devices ( + identifier TEXT NOT NULL, + device_id INTEGER NOT NULL, + PRIMARY KEY (identifier, device_id) + ) STRICT", + "PRAGMA user_version = 15"]); + } + return Promise.resolve(null); }); }); }); @@ -1421,11 +1431,34 @@ class Sqlite implements Persistence implements KeyValueStore { @HaxeCBridge.noemit public function getOmemoDeviceList(identifier:String): Promise<Array<Int>> { - return Promise.resolve([]); + return db.exec( + "SELECT device_id FROM omemo_devices WHERE identifier=?", + [identifier], + ).then(result -> { + return { iterator: () -> result }.map(row -> row.device_id).array(); + }); } @HaxeCBridge.noemit - public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Void { } + public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Promise<Array<Int>> { + return db.exec( + "DELETE FROM omemo_devices WHERE identifier=?", + [identifier], + ).then(_ -> { + if (deviceIds.length == 0) { + return Promise.resolve(deviceIds); + } + + final placeholders = deviceIds.map(_ -> "(?,?)").join(", "); + final params = deviceIds.flatMap( + (deviceId) -> ([identifier, deviceId] : Array<Dynamic>) + ); + db.exec( + "INSERT INTO omemo_devices (identifier, device_id) VALUES " + placeholders, + params, + ).then(_ -> deviceIds); + }); + } @HaxeCBridge.noemit public function storeOmemoPreKey(identifier:String, keyId:Int, keyPair:PreKeyPair):Void { } diff --git a/test/TestSqlite.hx b/test/TestSqlite.hx index d9a9fd3..6db1098 100644 --- a/test/TestSqlite.hx +++ b/test/TestSqlite.hx @@ -1366,4 +1366,42 @@ class TestSqlite extends utest.Test { }); } + public function testGetOmemoDeviceListNotFound(async: Async) { + persistence + .getOmemoDeviceList("devices-notfound@example.com") + .then(result -> { + Assert.same([], result); + async.done(); + }) + .catchError(e -> { + Assert.fail(Std.string(e)); + async.done(); + }); + } + + public function testOmemoDeviceList(async: Async) { + final identifier = "devices-existing@example.com"; + persistence + .storeOmemoDeviceList(identifier, [1, 2, 3]) + .then(_ -> persistence.getOmemoDeviceList(identifier)) + .then(initial -> { + Assert.same([1, 2, 3], initial); + return persistence.storeOmemoDeviceList(identifier, [4, 5]); + }) + .then(_ -> persistence.getOmemoDeviceList(identifier)) + .then(replaced -> { + Assert.same([4, 5], replaced); + return persistence.storeOmemoDeviceList(identifier, []); + }) + .then(_ -> persistence.getOmemoDeviceList(identifier)) + .then(cleared -> { + Assert.same([], cleared); + async.done(); + }) + .catchError(e -> { + Assert.fail(Std.string(e)); + async.done(); + }); + } + } diff --git a/test/persistence-tests.ts b/test/persistence-tests.ts index 8e74c03..61387af 100644 --- a/test/persistence-tests.ts +++ b/test/persistence-tests.ts @@ -1542,4 +1542,58 @@ export function sharedPersistenceTests(test: PersistenceTest) { expect(result.loadedPrivKey).toEqual([0, 1, 2, 127, 128, 255]); expect(result.loadedPubKey).toEqual([255, 128, 127, 2, 1, 0]); }); + + test("getOmemoDeviceList returns an empty list when none is stored", async ({ + page, + persistence, + }) => { + const result = await page.evaluate( + async (persistence) => + persistence.getOmemoDeviceList( + "omemo-devices-not-found@example.com", + ), + persistence, + ); + + expect(result).toEqual([]); + }); + + test("storeOmemoDeviceList replaces and clears the device list", async ({ + page, + persistence, + }) => { + const identifier = "omemo-devices-existing@example.com"; + const initialDeviceIds = [12345, 67890]; + const replacementDeviceIds = [24680]; + const result = await page.evaluate( + async ({ + persistence, + identifier, + initialDeviceIds, + replacementDeviceIds, + }) => { + await persistence.storeOmemoDeviceList(identifier, initialDeviceIds); + const initial = await persistence.getOmemoDeviceList(identifier); + await persistence.storeOmemoDeviceList( + identifier, + replacementDeviceIds, + ); + const afterReplace = await persistence.getOmemoDeviceList(identifier); + await persistence.storeOmemoDeviceList(identifier, []); + const afterClear = await persistence.getOmemoDeviceList(identifier); + + return { initial, afterReplace, afterClear }; + }, + { + persistence, + identifier, + initialDeviceIds, + replacementDeviceIds, + }, + ); + + expect(result.initial).toEqual(initialDeviceIds); + expect(result.afterReplace).toEqual(replacementDeviceIds); + expect(result.afterClear).toEqual([]); + }); }