DEV: Fix test that checks for leaked plaintexts.

DEV: Fix test that checks for leaked plaintexts.

During testing, the post is sometimes saved twice, which calls the save hook twice, generating two different encryption keys and producing two ciphertexts. This is most probably caused by a bug in mocks, fixture or the state is not properly reset after previous tests.

The test would fail with an OperationError which was raised because the post would be decrypted using the wrong key. When saving, only the last ciphertext would persist, but putTopicKey saved the first key, which caused an incompatibility.

The failure was reproduced only in the ‘discourse_test’ Docker image.

This commit also updates to the latest Draft API.

diff --git a/assets/javascripts/discourse/initializers/hook-draft.js.es6 b/assets/javascripts/discourse/initializers/hook-draft.js.es6
index f521d2e..28865ab 100644
--- a/assets/javascripts/discourse/initializers/hook-draft.js.es6
+++ b/assets/javascripts/discourse/initializers/hook-draft.js.es6
@@ -36,7 +36,7 @@ export default {
     }
 
     Draft.reopenClass({
-      save(draftKey, sequence, data) {
+      save(draftKey, sequence, data, clientId) {
         if (!container || container.isDestroyed || container.isDestroying) {
           // Since at this point we cannot be sure if it is an encrypted
           // topic or not, the draft is simply discarded.
@@ -56,7 +56,7 @@ export default {
         if (encrypted) {
           data = filterObjectKeys(data, ALLOWED_DRAFT_FIELDS);
           if (!data.title && !data.reply) {
-            return _super.call(this, draftKey, sequence, data);
+            return _super.call(this, ...arguments);
           }
 
           const topicKey = generateKey();
@@ -79,7 +79,7 @@ export default {
             ([title, reply, key]) => {
               data.title = title;
               data.reply = key + "\n" + reply;
-              return _super.call(this, draftKey, sequence, data);
+              return _super.call(this, draftKey, sequence, data, clientId);
             }
           );
         }
diff --git a/assets/javascripts/lib/discourse.js.es6 b/assets/javascripts/lib/discourse.js.es6
index dfa39be..6850b74 100644
--- a/assets/javascripts/lib/discourse.js.es6
+++ b/assets/javascripts/lib/discourse.js.es6
@@ -102,7 +102,7 @@ export function getUserIdentities(usernames) {
  * @param {String} key
  */
 export function putTopicKey(topicId, key) {
-  if (topicId && key && !topicKeys[topicId]) {
+  if (topicId && key) {
     topicKeys[topicId] = key;
   }
 }
diff --git a/test/javascripts/acceptance/encrypt-test.js.es6 b/test/javascripts/acceptance/encrypt-test.js.es6
index c781de5..f3cbd4f 100644
--- a/test/javascripts/acceptance/encrypt-test.js.es6
+++ b/test/javascripts/acceptance/encrypt-test.js.es6
@@ -199,9 +199,20 @@ test("posting does not leak plaintext", async assert => {
   await click(".reply-details a");
 
   requests = [];
-  await fillIn("#reply-title", `Some hidden message ${PLAINTEXT}`);
-  await fillIn(".d-editor-input", `Hello, world! ${PLAINTEXT}`.repeat(42));
-  await wait(() => requests.includes("/posts"), () => click("button.create"));
+  let waiting = setTimeout(() => (waiting = null), 3000);
+  await wait(
+    () => requests.includes("/draft.json") || !waiting,
+    async () => {
+      await fillIn("#reply-title", `Some hidden message ${PLAINTEXT}`);
+      await fillIn(".d-editor-input", `Hello, world! ${PLAINTEXT}`.repeat(42));
+    }
+  );
+
+  requests = [];
+  await wait(
+    () => requests.includes("/posts") && requests.includes("/encrypt/post"),
+    () => click("button.create")
+  );
 
   globalAssert = null;
 });

GitHub sha: 92202e70

1 Like

I believe the same weird thing happens when saving drafts too because the number of requests to /draft.json is variable (it could also be related to debouncing).

This code basically waits 3 seconds (1 second more than the debounce period) or one request to /draft.json.