Repository: Yoctol/bottender
Pull request #306 (2018-07-12 07:46:27)

skip and show warning when Messenger echo delivery read event
#296

codecov-io: # [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/306?src=pr&el=h1) Report
> Merging [#306](https://codecov.io/gh/Yoctol/bottender/pull/306?src=pr&el=desc) into [master](https://codecov.io/gh/Yoctol/bottender/commit/852db7011a8aa659c2a8e7954ace33fa5535675c?src=pr&el=desc) will **decrease** coverage by `0.1%`.
> The diff coverage is `33.33%`.

[![Impacted file tree graph](https://codecov.io/gh/Yoctol/bottender/pull/306/graphs/tree.svg?height=150&width=650&src=pr&token=JX3EGtxYH3)](https://codecov.io/gh/Yoctol/bottender/pull/306?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #306      +/-   ##
==========================================
- Coverage   90.28%   90.18%   -0.11%     
==========================================
  Files         112      112              
  Lines        3294     3300       +6     
  Branches      780      782       +2     
==========================================
+ Hits         2974     2976       +2     
- Misses        280      284       +4     
  Partials       40       40
```


| [Impacted Files](https://codecov.io/gh/Yoctol/bottender/pull/306?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/context/MessengerContext.js](https://codecov.io/gh/Yoctol/bottender/pull/306/diff?src=pr&el=tree#diff-c3JjL2NvbnRleHQvTWVzc2VuZ2VyQ29udGV4dC5qcw==) | `95.41% <33.33%> (-3.62%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Yoctol/bottender/pull/306?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/306?src=pr&el=footer). Last update [852db70...eb40e84](https://codecov.io/gh/Yoctol/bottender/pull/306?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/context/MessengerContext.js b/src/context/MessengerContext.js
index 749645c0..0e49534d 100644
--- a/src/context/MessengerContext.js
+++ b/src/context/MessengerContext.js
@@ -89,6 +89,14 @@ class MessengerContext extends Context implements PlatformContext {
       return;
     }
 
+    if (this._event.isEcho || this._event.isDelivery || this._event.isRead) {
+      warning(
+        false,
+        'sendText: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+      );
+      return;
+    }
+
     this._isHandled = true;
 
     const messageType = options && options.tag ? 'MESSAGE_TAG' : 'RESPONSE';
@@ -428,7 +436,7 @@ class MessengerContext extends Context implements PlatformContext {
     if (!this._session) {
       warning(
         false,
-        'dissociateLabel: should not be called in context without session'
+        'getAssociatedLabels: should not be called in context without session'
       );
       return;
     }
@@ -482,6 +490,13 @@ sendMethods.forEach(([method, len]) => {
         return;
       }
 
+      if (this._event.isEcho || this._event.isDelivery || this._event.isRead) {
+        warning(
+          false,
+          `${method}: calling Send APIs in \`message_reads\`(event.isRead), \`message_deliveries\`(event.isDelivery) or \`message_echoes\`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.`
+        );
+        return;
+      }
       this._isHandled = true;
 
       const options = args[len - 2];


Test patch:
diff --git a/src/context/__tests__/MessengerContext.spec.js b/src/context/__tests__/MessengerContext.spec.js
index feb6fe19..ab1e9b95 100644
--- a/src/context/__tests__/MessengerContext.spec.js
+++ b/src/context/__tests__/MessengerContext.spec.js
@@ -1,3 +1,5 @@
+import { delivery, read, echoMessage as echo } from './MessengerEvent.spec';
+
 jest.mock('delay');
 jest.mock('messaging-api-messenger');
 jest.mock('warning');
@@ -22,7 +24,7 @@ afterEach(() => {
   jest.useFakeTimers();
 });
 
-const rawEvent = {
+const _rawEvent = {
   sender: { id: '1423587017700273' },
   recipient: { id: '404217156637689' },
   timestamp: 1491796363181,
@@ -40,9 +42,10 @@ const userSession = {
 };
 
 const setup = (
-  { session, customAccessToken } = {
+  { session = userSession, customAccessToken, rawEvent = _rawEvent } = {
     session: userSession,
     customAccessToken: undefined,
+    _rawEvent,
   }
 ) => {
   const client = MessengerClient.connect();
@@ -150,7 +153,46 @@ describe('#sendText', () => {
 
     await context.sendText();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'sendText: should not be called in context without session'
+    );
+    expect(client.sendText).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    await context.sendText('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendText: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendText).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    await context.sendText('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendText: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendText).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    await context.sendText('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendText: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
     expect(client.sendText).not.toBeCalled();
   });
 });
@@ -190,6 +232,74 @@ describe('#sendAttachment', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    await context.sendAttachment({
+      type: 'image',
+      payload: {
+        url: 'https://example.com/pic.png',
+      },
+    });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAttachment: should not be called in context without session'
+    );
+    expect(client.sendAttachment).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    await context.sendAttachment({
+      type: 'image',
+      payload: {
+        url: 'https://example.com/pic.png',
+      },
+    });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAttachment: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAttachment).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    await context.sendAttachment({
+      type: 'image',
+      payload: {
+        url: 'https://example.com/pic.png',
+      },
+    });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAttachment: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAttachment).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    await context.sendAttachment({
+      type: 'image',
+      payload: {
+        url: 'https://example.com/pic.png',
+      },
+    });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAttachment: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAttachment).not.toBeCalled();
+  });
 });
 
 describe('#sendImage', () => {
@@ -216,7 +326,46 @@ describe('#sendImage', () => {
 
     await context.sendImage('xxx.com');
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'sendImage: should not be called in context without session'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    await context.sendImage('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendImage: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    await context.sendImage('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendImage: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    await context.sendImage('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendImage: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
     expect(client.sendImage).not.toBeCalled();
   });
 });
@@ -239,6 +388,54 @@ describe('#sendAudio', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    await context.sendAudio('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAudio: should not be called in context without session'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    await context.sendAudio('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAudio: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    await context.sendAudio('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAudio: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    await context.sendAudio('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAudio: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
 });
 
 describe('#sendVideo', () => {
@@ -259,6 +456,54 @@ describe('#sendVideo', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    await context.sendVideo('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendVideo: should not be called in context without session'
+    );
+    expect(client.sendVideo).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    await context.sendVideo('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendVideo: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendVideo).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    await context.sendVideo('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendVideo: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendVideo).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    await context.sendVideo('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendVideo: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendVideo).not.toBeCalled();
+  });
 });
 
 describe('#sendFile', () => {
@@ -279,6 +524,54 @@ describe('#sendFile', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    await context.sendFile('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendFile: should not be called in context without session'
+    );
+    expect(client.sendFile).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    await context.sendFile('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendFile: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendFile).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    await context.sendFile('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendFile: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendFile).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    await context.sendFile('xxx.com');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendFile: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendFile).not.toBeCalled();
+  });
 });
 
 describe('#sendTemplate', () => {
@@ -379,6 +672,66 @@ describe('#sendGenericTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const elements = {};
+    const ratio = '';
+
+    await context.sendGenericTemplate(elements, { image_aspect_ratio: ratio });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendGenericTemplate: should not be called in context without session'
+    );
+    expect(client.sendGenericTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const elements = {};
+    const ratio = '';
+
+    await context.sendGenericTemplate(elements, { image_aspect_ratio: ratio });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendGenericTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendGenericTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const elements = {};
+    const ratio = '';
+
+    await context.sendGenericTemplate(elements, { image_aspect_ratio: ratio });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendGenericTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendGenericTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const elements = {};
+    const ratio = '';
+
+    await context.sendGenericTemplate(elements, { image_aspect_ratio: ratio });
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendGenericTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendGenericTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendButtonTemplate', () => {
@@ -408,6 +761,62 @@ describe('#sendButtonTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const buttons = [];
+
+    await context.sendButtonTemplate('yayaya', buttons);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendButtonTemplate: should not be called in context without session'
+    );
+    expect(client.sendButtonTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const buttons = [];
+
+    await context.sendButtonTemplate('yayaya', buttons);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendButtonTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendButtonTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const buttons = [];
+
+    await context.sendButtonTemplate('yayaya', buttons);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendButtonTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendButtonTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const buttons = [];
+
+    await context.sendButtonTemplate('yayaya', buttons);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendButtonTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendButtonTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendListTemplate', () => {
@@ -442,6 +851,66 @@ describe('#sendListTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const elements = [];
+    const buttons = [];
+
+    await context.sendListTemplate(elements, buttons, 'large');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendListTemplate: should not be called in context without session'
+    );
+    expect(client.sendListTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const elements = [];
+    const buttons = [];
+
+    await context.sendListTemplate(elements, buttons, 'large');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendListTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendListTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const elements = [];
+    const buttons = [];
+
+    await context.sendListTemplate(elements, buttons, 'large');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendListTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendListTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const elements = [];
+    const buttons = [];
+
+    await context.sendListTemplate(elements, buttons, 'large');
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendListTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendListTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendOpenGraphTemplate', () => {
@@ -462,25 +931,91 @@ describe('#sendOpenGraphTemplate', () => {
       },
     ]);
 
-    expect(client.sendOpenGraphTemplate).toBeCalledWith(
-      session.user.id,
-      [
-        {
-          media_type: 'image',
-          attachment_id: '1854626884821032',
-          buttons: [
-            {
-              type: 'web_url',
-              url: 'https://en.wikipedia.org/wiki/Rickrolling',
-              title: 'View Website',
-            },
-          ],
-        },
-      ],
-      {
-        messaging_type: 'RESPONSE',
-      }
+    expect(client.sendOpenGraphTemplate).toBeCalledWith(
+      session.user.id,
+      [
+        {
+          media_type: 'image',
+          attachment_id: '1854626884821032',
+          buttons: [
+            {
+              type: 'web_url',
+              url: 'https://en.wikipedia.org/wiki/Rickrolling',
+              title: 'View Website',
+            },
+          ],
+        },
+      ],
+      {
+        messaging_type: 'RESPONSE',
+      }
+    );
+  });
+
+  it('should mark context as handled', async () => {
+    const { context } = setup();
+
+    const elements = [];
+
+    await context.sendOpenGraphTemplate(elements);
+
+    expect(context.isHandled).toBe(true);
+  });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const elements = [];
+
+    await context.sendOpenGraphTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendOpenGraphTemplate: should not be called in context without session'
+    );
+    expect(client.sendOpenGraphTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const elements = [];
+
+    await context.sendOpenGraphTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendOpenGraphTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendOpenGraphTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const elements = [];
+
+    await context.sendOpenGraphTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendOpenGraphTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendOpenGraphTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const elements = [];
+
+    await context.sendOpenGraphTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendOpenGraphTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
     );
+    expect(client.sendOpenGraphTemplate).not.toBeCalled();
   });
 });
 
@@ -520,6 +1055,72 @@ describe('#sendMediaTemplate', () => {
       }
     );
   });
+
+  it('should mark context as handled', async () => {
+    const { context } = setup();
+
+    const elements = [];
+
+    await context.sendMediaTemplate(elements);
+
+    expect(context.isHandled).toBe(true);
+  });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const elements = [];
+
+    await context.sendMediaTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendMediaTemplate: should not be called in context without session'
+    );
+    expect(client.sendMediaTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const elements = [];
+
+    await context.sendMediaTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendMediaTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendMediaTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const elements = [];
+
+    await context.sendMediaTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendMediaTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendMediaTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const elements = [];
+
+    await context.sendMediaTemplate(elements);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendMediaTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendMediaTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendReceiptTemplate', () => {
@@ -548,6 +1149,62 @@ describe('#sendReceiptTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const receipt = {};
+
+    await context.sendReceiptTemplate(receipt);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendReceiptTemplate: should not be called in context without session'
+    );
+    expect(client.sendReceiptTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const receipt = {};
+
+    await context.sendReceiptTemplate(receipt);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendReceiptTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendReceiptTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const receipt = {};
+
+    await context.sendReceiptTemplate(receipt);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendReceiptTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendReceiptTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const receipt = {};
+
+    await context.sendReceiptTemplate(receipt);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendReceiptTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendReceiptTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendAirlineBoardingPassTemplate', () => {
@@ -576,6 +1233,62 @@ describe('#sendAirlineBoardingPassTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const boardingPass = {};
+
+    await context.sendAirlineBoardingPassTemplate(boardingPass);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineBoardingPassTemplate: should not be called in context without session'
+    );
+    expect(client.sendAirlineBoardingPassTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const boardingPass = {};
+
+    await context.sendAirlineBoardingPassTemplate(boardingPass);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineBoardingPassTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineBoardingPassTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const boardingPass = {};
+
+    await context.sendAirlineBoardingPassTemplate(boardingPass);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineBoardingPassTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineBoardingPassTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const boardingPass = {};
+
+    await context.sendAirlineBoardingPassTemplate(boardingPass);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineBoardingPassTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineBoardingPassTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendAirlineCheckinTemplate', () => {
@@ -604,6 +1317,62 @@ describe('#sendAirlineCheckinTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const checkin = {};
+
+    await context.sendAirlineCheckinTemplate(checkin);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineCheckinTemplate: should not be called in context without session'
+    );
+    expect(client.sendAirlineCheckinTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const checkin = {};
+
+    await context.sendAirlineCheckinTemplate(checkin);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineCheckinTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineCheckinTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const checkin = {};
+
+    await context.sendAirlineCheckinTemplate(checkin);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineCheckinTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineCheckinTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const checkin = {};
+
+    await context.sendAirlineCheckinTemplate(checkin);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineCheckinTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineCheckinTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendAirlineItineraryTemplate', () => {
@@ -632,6 +1401,62 @@ describe('#sendAirlineItineraryTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const itinerary = {};
+
+    await context.sendAirlineItineraryTemplate(itinerary);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineItineraryTemplate: should not be called in context without session'
+    );
+    expect(client.sendAirlineItineraryTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const itinerary = {};
+
+    await context.sendAirlineItineraryTemplate(itinerary);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineItineraryTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineItineraryTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const itinerary = {};
+
+    await context.sendAirlineItineraryTemplate(itinerary);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineItineraryTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineItineraryTemplate).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const itinerary = {};
+
+    await context.sendAirlineItineraryTemplate(itinerary);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineItineraryTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendAirlineItineraryTemplate).not.toBeCalled();
+  });
 });
 
 describe('#sendAirlineFlightUpdateTemplate', () => {
@@ -660,6 +1485,62 @@ describe('#sendAirlineFlightUpdateTemplate', () => {
 
     expect(context.isHandled).toBe(true);
   });
+
+  it('should call warning and not to send if dont have session', async () => {
+    const { context, client } = setup({ session: false });
+
+    const flightUpdate = {};
+
+    await context.sendAirlineFlightUpdateTemplate(flightUpdate);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineFlightUpdateTemplate: should not be called in context without session'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with read event', async () => {
+    const { context, client } = setup({ rawEvent: read });
+
+    const flightUpdate = {};
+
+    await context.sendAirlineFlightUpdateTemplate(flightUpdate);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineFlightUpdateTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with delivery event', async () => {
+    const { context, client } = setup({ rawEvent: delivery });
+
+    const flightUpdate = {};
+
+    await context.sendAirlineFlightUpdateTemplate(flightUpdate);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineFlightUpdateTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
+
+  it('should call warning and not to send if created with echo event', async () => {
+    const { context, client } = setup({ rawEvent: echo });
+
+    const flightUpdate = {};
+
+    await context.sendAirlineFlightUpdateTemplate(flightUpdate);
+
+    expect(warning).toBeCalledWith(
+      false,
+      'sendAirlineFlightUpdateTemplate: calling Send APIs in `message_reads`(event.isRead), `message_deliveries`(event.isDelivery) or `message_echoes`(event.isEcho) events may cause endless self-responding, so they are ignored by default.\nYou may like to turn off subsctiption of those events or handle them without Send APIs.'
+    );
+    expect(client.sendImage).not.toBeCalled();
+  });
 });
 
 describe('#getUserProfile', () => {
@@ -715,7 +1596,10 @@ describe('#getUserProfile', () => {
 
     await context.getUserProfile();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'getUserProfile: should not be called in context without session'
+    );
     expect(client.getUserProfile).not.toBeCalled();
   });
 });
@@ -765,7 +1649,10 @@ describe('#sendSenderAction', () => {
 
     await context.sendSenderAction('typing_on');
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'sendSenderAction: should not be called in context without session'
+    );
     expect(client.sendSenderAction).not.toBeCalled();
   });
 });
@@ -807,7 +1694,10 @@ describe('#typingOn', () => {
 
     await context.typingOn();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'typingOn: should not be called in context without session'
+    );
     expect(client.typingOn).not.toBeCalled();
   });
 });
@@ -849,7 +1739,10 @@ describe('#typingOff', () => {
 
     await context.typingOff();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'typingOff: should not be called in context without session'
+    );
     expect(client.typingOff).not.toBeCalled();
   });
 });
@@ -891,7 +1784,10 @@ describe('#markSeen', () => {
 
     await context.markSeen();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'markSeen: should not be called in context without session'
+    );
     expect(client.markSeen).not.toBeCalled();
   });
 });
@@ -955,7 +1851,10 @@ describe('#passThreadControl', () => {
 
     await context.passThreadControl(263902037430900);
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'passThreadControl: should not be called in context without session'
+    );
     expect(client.passThreadControl).not.toBeCalled();
   });
 });
@@ -997,7 +1896,10 @@ describe('#passThreadControlToPageInbox', () => {
 
     await context.passThreadControlToPageInbox();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'passThreadControlToPageInbox: should not be called in context without session'
+    );
     expect(client.passThreadControlToPageInbox).not.toBeCalled();
   });
 });
@@ -1039,7 +1941,10 @@ describe('#takeThreadControl', () => {
 
     await context.takeThreadControl();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'takeThreadControl: should not be called in context without session'
+    );
     expect(client.takeThreadControl).not.toBeCalled();
   });
 });
@@ -1081,7 +1986,10 @@ describe('#requestThreadControl', () => {
 
     await context.requestThreadControl();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'requestThreadControl: should not be called in context without session'
+    );
     expect(client.requestThreadControl).not.toBeCalled();
   });
 });
@@ -1115,7 +2023,10 @@ describe('#getThreadOwner', () => {
 
     await context.getThreadOwner();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'getThreadOwner: should not be called in context without session'
+    );
     expect(client.getThreadOwner).not.toBeCalled();
   });
 });
@@ -1153,7 +2064,10 @@ describe('#associateLabel', () => {
 
     await context.associateLabel(1712444532121303);
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'associateLabel: should not be called in context without session'
+    );
     expect(client.associateLabel).not.toBeCalled();
   });
 });
@@ -1191,7 +2105,10 @@ describe('#dissociateLabel', () => {
 
     await context.dissociateLabel(1712444532121303);
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'dissociateLabel: should not be called in context without session'
+    );
     expect(client.dissociateLabel).not.toBeCalled();
   });
 });
@@ -1267,7 +2184,10 @@ describe('#getAssociatedLabels', () => {
 
     await context.getAssociatedLabels();
 
-    expect(warning).toBeCalled();
+    expect(warning).toBeCalledWith(
+      false,
+      'getAssociatedLabels: should not be called in context without session'
+    );
     expect(client.getAssociatedLabels).not.toBeCalled();
   });
 });
diff --git a/src/context/__tests__/MessengerEvent.spec.js b/src/context/__tests__/MessengerEvent.spec.js
index 22346feb..78f35ffb 100644
--- a/src/context/__tests__/MessengerEvent.spec.js
+++ b/src/context/__tests__/MessengerEvent.spec.js
@@ -240,7 +240,7 @@ const quickReplyMessage = {
   },
 };
 
-const delivery = {
+export const delivery = {
   sender: {
     id: '404217156637689',
   },
@@ -254,7 +254,7 @@ const delivery = {
   },
 };
 
-const read = {
+export const read = {
   sender: {
     id: '404217156637689',
   },
@@ -268,7 +268,7 @@ const read = {
   },
 };
 
-const echoMessage = {
+export const echoMessage = {
   sender: {
     id: '404217156637689',
   },