From 66ef0e4adb05314c30ce8bed10bc388c36476290 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:45:13 +0400 Subject: [PATCH 01/17] docs: add Suppressions example to README for enhanced API usage clarity --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bc559d4..6c7b59a 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Refer to the [`examples`](examples) folder for the source code of this and other - [Advanced](examples/sending/everything.ts) - [Minimal](examples/sending/minimal.ts) - [Send email using template](examples/sending/template.ts) + - [Suppressions](examples/sending/suppressions.ts) - [Nodemailer transport](examples/sending/transport.ts) ### Batch Sending API From 31718237c515b058d1ef3d44f262017769d84730 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:46:45 +0400 Subject: [PATCH 02/17] examples: add suppressions example to demonstrate MailtrapClient functionality --- examples/sending/suppressions.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 examples/sending/suppressions.ts diff --git a/examples/sending/suppressions.ts b/examples/sending/suppressions.ts new file mode 100644 index 0000000..010a24e --- /dev/null +++ b/examples/sending/suppressions.ts @@ -0,0 +1,30 @@ +import { MailtrapClient } from "mailtrap"; + +const TOKEN = ""; +const ACCOUNT_ID = ""; + +const client = new MailtrapClient({ + token: TOKEN, + accountId: ACCOUNT_ID +}); + +async function suppressionsFlow() { + // Get all suppressions + const allSuppressions = await client.suppressions.getList(); + console.log("All suppressions:", allSuppressions); + + // Get suppressions filtered by email + const filteredSuppressions = await client.suppressions.getList("test@example.com"); + console.log("Filtered suppressions:", filteredSuppressions); + + // Delete a suppression by ID (if any exist) + if (allSuppressions.length > 0) { + const suppressionToDelete = allSuppressions[0]; + await client.suppressions.delete(suppressionToDelete.id); + console.log(`Suppression ${suppressionToDelete.id} deleted successfully`); + } else { + console.log("No suppressions found to delete"); + } +} + +suppressionsFlow().catch(console.error); From 4c8580b45fe181051869be9cf91bb8446edf138d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:46:51 +0400 Subject: [PATCH 03/17] test: add unit tests for MailtrapClient suppressions getter to validate error handling and initialization --- src/__tests__/lib/mailtrap-client.test.ts | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/__tests__/lib/mailtrap-client.test.ts b/src/__tests__/lib/mailtrap-client.test.ts index f92496a..90ba4ad 100644 --- a/src/__tests__/lib/mailtrap-client.test.ts +++ b/src/__tests__/lib/mailtrap-client.test.ts @@ -12,6 +12,7 @@ import TestingAPI from "../../lib/api/Testing"; import ContactLists from "../../lib/api/ContactLists"; import Contacts from "../../lib/api/Contacts"; import TemplatesBaseAPI from "../../lib/api/Templates"; +import SuppressionsBaseAPI from "../../lib/api/Suppressions"; const { ERRORS, CLIENT_SETTINGS } = CONFIG; const { TESTING_ENDPOINT, BULK_ENDPOINT, SENDING_ENDPOINT } = CLIENT_SETTINGS; @@ -809,5 +810,31 @@ describe("lib/mailtrap-client: ", () => { expect(templatesClient).toBeInstanceOf(TemplatesBaseAPI); }); }); + + describe("get suppressions(): ", () => { + it("rejects with Mailtrap error, when `accountId` is missing.", () => { + const client = new MailtrapClient({ + token: "MY_API_TOKEN", + }); + expect.assertions(1); + + try { + client.suppressions; + } catch (error) { + expect(error).toEqual(new MailtrapError(ACCOUNT_ID_MISSING)); + } + }); + + it("returns suppressions API object when accountId is provided.", () => { + const client = new MailtrapClient({ + token: "MY_API_TOKEN", + accountId: 10, + }); + expect.assertions(1); + + const suppressionsClient = client.suppressions; + expect(suppressionsClient).toBeInstanceOf(SuppressionsBaseAPI); + }); + }); }); }); From f0e39078ad0e400374f0edb7796a456f5c752039 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:46:56 +0400 Subject: [PATCH 04/17] test: add unit tests for SuppressionsBaseAPI to verify initialization and method presence --- src/__tests__/lib/api/Suppressions.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/__tests__/lib/api/Suppressions.test.ts diff --git a/src/__tests__/lib/api/Suppressions.test.ts b/src/__tests__/lib/api/Suppressions.test.ts new file mode 100644 index 0000000..920fa7a --- /dev/null +++ b/src/__tests__/lib/api/Suppressions.test.ts @@ -0,0 +1,17 @@ +import axios from "axios"; + +import SuppressionsBaseAPI from "../../../lib/api/Suppressions"; + +describe("lib/api/Suppressions: ", () => { + const accountId = 100; + const suppressionsAPI = new SuppressionsBaseAPI(axios, accountId); + + describe("class SuppressionsBaseAPI(): ", () => { + describe("init: ", () => { + it("initalizes with all necessary params.", () => { + expect(suppressionsAPI).toHaveProperty("getList"); + expect(suppressionsAPI).toHaveProperty("delete"); + }); + }); + }); +}); From 5f88ec23e2355f47b81319e97bae5f80db5ddd28 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:47:01 +0400 Subject: [PATCH 05/17] test: add comprehensive unit tests for SuppressionsApi to validate getList and delete methods, including error handling --- .../lib/api/resources/Suppression.test.ts | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 src/__tests__/lib/api/resources/Suppression.test.ts diff --git a/src/__tests__/lib/api/resources/Suppression.test.ts b/src/__tests__/lib/api/resources/Suppression.test.ts new file mode 100644 index 0000000..55951ce --- /dev/null +++ b/src/__tests__/lib/api/resources/Suppression.test.ts @@ -0,0 +1,170 @@ +import axios from "axios"; +import AxiosMockAdapter from "axios-mock-adapter"; + +import SuppressionsApi from "../../../../lib/api/resources/Suppressions"; +import handleSendingError from "../../../../lib/axios-logger"; +import MailtrapError from "../../../../lib/MailtrapError"; +import { Suppression } from "../../../../types/api/suppressions"; + +import CONFIG from "../../../../config"; + +const { CLIENT_SETTINGS } = CONFIG; +const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; + +describe("lib/api/resources/Suppressions: ", () => { + let mock: AxiosMockAdapter; + const accountId = 100; + const suppressionsAPI = new SuppressionsApi(axios, accountId); + + const mockSuppression: Suppression = { + id: "1", + email: "test@example.com", + type: "hard bounce", + created_at: "2023-01-01T00:00:00Z", + sending_stream: "transactional", + domain_name: "example.com", + message_bounce_category: "bad_mailbox", + message_category: "test", + message_client_ip: "192.168.1.1", + message_created_at: "2023-01-01T00:00:00Z", + message_outgoing_ip: "10.0.0.1", + message_recipient_mx_name: "mx.example.com", + message_sender_email: "sender@example.com", + message_subject: "Test Email", + }; + + const mockSuppressions: Suppression[] = [ + { + id: "1", + email: "test1@example.com", + type: "hard bounce", + created_at: "2023-01-01T00:00:00Z", + sending_stream: "transactional", + domain_name: "example.com", + message_bounce_category: "bad_mailbox", + message_category: "test", + message_client_ip: "192.168.1.1", + message_created_at: "2023-01-01T00:00:00Z", + message_outgoing_ip: "10.0.0.1", + message_recipient_mx_name: "mx.example.com", + message_sender_email: "sender@example.com", + message_subject: "Test Email 1", + }, + { + id: "2", + email: "test2@example.com", + type: "spam complaint", + created_at: "2023-01-02T00:00:00Z", + sending_stream: "bulk", + domain_name: "example.com", + message_bounce_category: null, + message_category: "promotional", + message_client_ip: "192.168.1.2", + message_created_at: "2023-01-02T00:00:00Z", + message_outgoing_ip: "10.0.0.2", + message_recipient_mx_name: "mx.example.com", + message_sender_email: "sender@example.com", + message_subject: "Test Email 2", + }, + ]; + + describe("class SuppressionsApi(): ", () => { + describe("init: ", () => { + it("initializes with all necessary params.", () => { + expect(suppressionsAPI).toHaveProperty("getList"); + expect(suppressionsAPI).toHaveProperty("delete"); + }); + }); + }); + + beforeAll(() => { + axios.interceptors.response.use( + (response) => response.data, + handleSendingError + ); + mock = new AxiosMockAdapter(axios); + }); + + afterEach(() => { + mock.reset(); + }); + + describe("getList(): ", () => { + it("successfully gets all suppressions.", async () => { + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`; + + expect.assertions(2); + + mock.onGet(endpoint).reply(200, mockSuppressions); + const result = await suppressionsAPI.getList(); + + expect(mock.history.get[0].url).toEqual(endpoint); + expect(result).toEqual(mockSuppressions); + }); + + it("successfully gets suppressions filtered by email.", async () => { + const email = "test@example.com"; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`; + + expect.assertions(3); + + mock.onGet(endpoint, { params: { email } }).reply(200, [mockSuppression]); + const result = await suppressionsAPI.getList(email); + + expect(mock.history.get[0].url).toEqual(endpoint); + expect(mock.history.get[0].params).toEqual({ email }); + expect(result).toEqual([mockSuppression]); + }); + + it("fails with error.", async () => { + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`; + const expectedErrorMessage = "Request failed with status code 400"; + + expect.assertions(2); + + mock.onGet(endpoint).reply(400, { error: expectedErrorMessage }); + + try { + await suppressionsAPI.getList(); + } catch (error) { + expect(error).toBeInstanceOf(MailtrapError); + if (error instanceof MailtrapError) { + expect(error.message).toEqual(expectedErrorMessage); + } + } + }); + }); + + describe("delete(): ", () => { + const suppressionId = "1"; + + it("successfully deletes a suppression.", async () => { + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`; + + expect.assertions(1); + + mock.onDelete(endpoint).reply(204); + await suppressionsAPI.delete(suppressionId); + + expect(mock.history.delete[0].url).toEqual(endpoint); + }); + + it("fails with error.", async () => { + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`; + const expectedErrorMessage = "Request failed with status code 404"; + + expect.assertions(2); + + mock.onDelete(endpoint).reply(404, { error: expectedErrorMessage }); + + try { + await suppressionsAPI.delete(suppressionId); + } catch (error) { + expect(error).toBeInstanceOf(MailtrapError); + if (error instanceof MailtrapError) { + expect(error.message).toEqual(expectedErrorMessage); + } + } + }); + }); +}); From 4912a5bda92d0ca8800dc91fd5689ee24ad56f7e Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:48:12 +0400 Subject: [PATCH 06/17] feat: add suppressions getter to MailtrapClient for managing suppressions with error handling --- src/lib/MailtrapClient.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib/MailtrapClient.ts b/src/lib/MailtrapClient.ts index 31e4785..a1987e4 100644 --- a/src/lib/MailtrapClient.ts +++ b/src/lib/MailtrapClient.ts @@ -22,6 +22,7 @@ import { BatchSendResponse, BatchSendRequest, } from "../types/mailtrap"; +import SuppressionsBaseAPI from "./api/Suppressions"; const { CLIENT_SETTINGS, ERRORS } = CONFIG; const { @@ -131,6 +132,9 @@ export default class MailtrapClient { return new ContactListsBaseAPI(this.axios, this.accountId); } + /** + * Getter for Templates API. + */ get templates() { if (!this.accountId) { throw new MailtrapError(ACCOUNT_ID_MISSING); @@ -139,6 +143,17 @@ export default class MailtrapClient { return new TemplatesBaseAPI(this.axios, this.accountId); } + /** + * Getter for Suppressions API. + */ + get suppressions() { + if (!this.accountId) { + throw new MailtrapError(ACCOUNT_ID_MISSING); + } + + return new SuppressionsBaseAPI(this.axios, this.accountId); + } + /** * Returns configured host. Checks if `bulk` and `sandbox` modes are activated simultaneously, * then reject with Mailtrap Error. From f576198c9aa40f4f52e0e67045c035a31561ab96 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:48:19 +0400 Subject: [PATCH 07/17] feat: implement SuppressionsBaseAPI for managing suppressions with Axios client --- src/lib/api/Suppressions.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/lib/api/Suppressions.ts diff --git a/src/lib/api/Suppressions.ts b/src/lib/api/Suppressions.ts new file mode 100644 index 0000000..c5da482 --- /dev/null +++ b/src/lib/api/Suppressions.ts @@ -0,0 +1,21 @@ +import { AxiosInstance } from "axios"; + +import SuppressionsApi from "./resources/Suppressions"; + +export default class SuppressionsBaseAPI { + private client: AxiosInstance; + + private accountId?: number; + + public getList: SuppressionsApi["getList"]; + + public delete: SuppressionsApi["delete"]; + + constructor(client: AxiosInstance, accountId?: number) { + this.client = client; + this.accountId = accountId; + const suppressions = new SuppressionsApi(this.client, this.accountId); + this.getList = suppressions.getList.bind(suppressions); + this.delete = suppressions.delete.bind(suppressions); + } +} From 43a42bd8c9f6da732b7d8d36b36ef144b561a76d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:48:25 +0400 Subject: [PATCH 08/17] feat: create SuppressionsApi class for managing suppressions with Axios, including methods for listing and deleting suppressions --- src/lib/api/resources/Suppressions.ts | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/lib/api/resources/Suppressions.ts diff --git a/src/lib/api/resources/Suppressions.ts b/src/lib/api/resources/Suppressions.ts new file mode 100644 index 0000000..276766d --- /dev/null +++ b/src/lib/api/resources/Suppressions.ts @@ -0,0 +1,40 @@ +import { AxiosInstance } from "axios"; + +import CONFIG from "../../../config"; +import { Suppression } from "../../../types/api/suppressions"; + +const { CLIENT_SETTINGS } = CONFIG; +const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; + +export default class SuppressionsApi { + private client: AxiosInstance; + + private accountId?: number; + + private suppressionsURL: string; + + constructor(client: AxiosInstance, accountId?: number) { + this.client = client; + this.accountId = accountId; + this.suppressionsURL = `${GENERAL_ENDPOINT}/api/accounts/${this.accountId}/suppressions`; + } + + /** + * List and search suppressions by email. The endpoint returns up to 1000 suppressions per request. + */ + public async getList(email?: string) { + return this.client.get(this.suppressionsURL, { + params: { email }, + }); + } + + /** + * Delete a suppression by ID. + * Mailtrap will no longer prevent sending to this email unless it's recorded in suppressions again. + */ + public async delete(id: string) { + return this.client.delete( + `${this.suppressionsURL}/${id}` + ); + } +} From 1520fdb19749a3cd6ef2819dbc155ab79ffcc6ee Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Sat, 21 Jun 2025 15:48:32 +0400 Subject: [PATCH 09/17] feat: add Suppression type definition for managing suppression data structure --- src/types/api/suppressions.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/types/api/suppressions.ts diff --git a/src/types/api/suppressions.ts b/src/types/api/suppressions.ts new file mode 100644 index 0000000..72b3d0b --- /dev/null +++ b/src/types/api/suppressions.ts @@ -0,0 +1,16 @@ +export type Suppression = { + id: string; + type: "hard bounce" | "spam complaint" | "unsubscription" | "manual import"; + created_at: string; + email: string; + sending_stream: "transactional" | "bulk"; + domain_name: string | null; + message_bounce_category: string | null; + message_category: string | null; + message_client_ip: string | null; + message_created_at: string | null; + message_outgoing_ip: string | null; + message_recipient_mx_name: string | null; + message_sender_email: string | null; + message_subject: string | null; +}; From 13b8ad9ed825f98b5d3adc6167a8401da56e96b6 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Tue, 1 Jul 2025 15:04:17 +0400 Subject: [PATCH 10/17] test: rename supression test to supressions --- .../api/resources/{Suppression.test.ts => Suppressions.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/__tests__/lib/api/resources/{Suppression.test.ts => Suppressions.test.ts} (100%) diff --git a/src/__tests__/lib/api/resources/Suppression.test.ts b/src/__tests__/lib/api/resources/Suppressions.test.ts similarity index 100% rename from src/__tests__/lib/api/resources/Suppression.test.ts rename to src/__tests__/lib/api/resources/Suppressions.test.ts From a2c3e6efab4785de02573a9a81726c7461cc531d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Tue, 1 Jul 2025 15:05:17 +0400 Subject: [PATCH 11/17] refactor: update getList method in SuppressionsApi to accept ListOptions for improved flexibility in querying suppressions --- src/lib/api/resources/Suppressions.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/api/resources/Suppressions.ts b/src/lib/api/resources/Suppressions.ts index 276766d..2c10529 100644 --- a/src/lib/api/resources/Suppressions.ts +++ b/src/lib/api/resources/Suppressions.ts @@ -1,7 +1,7 @@ import { AxiosInstance } from "axios"; import CONFIG from "../../../config"; -import { Suppression } from "../../../types/api/suppressions"; +import { ListOptions, Suppression } from "../../../types/api/suppressions"; const { CLIENT_SETTINGS } = CONFIG; const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; @@ -22,9 +22,13 @@ export default class SuppressionsApi { /** * List and search suppressions by email. The endpoint returns up to 1000 suppressions per request. */ - public async getList(email?: string) { + public async getList(options?: ListOptions) { + const params = { + ...(options?.email && { email: options.email }), + }; + return this.client.get(this.suppressionsURL, { - params: { email }, + params, }); } From a8ec09ed4ee9e48a37f39ebbdec8ddfe61ca48b4 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Tue, 1 Jul 2025 15:05:25 +0400 Subject: [PATCH 12/17] feat: add ListOptions type to enhance flexibility in suppression queries --- src/types/api/suppressions.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/api/suppressions.ts b/src/types/api/suppressions.ts index 72b3d0b..e707739 100644 --- a/src/types/api/suppressions.ts +++ b/src/types/api/suppressions.ts @@ -14,3 +14,7 @@ export type Suppression = { message_sender_email: string | null; message_subject: string | null; }; + +export type ListOptions = { + email?: string; +}; From 88bff569cbd58d92d38427831991d64b51c7b6ce Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 2 Jul 2025 18:27:31 +0400 Subject: [PATCH 13/17] test: update Suppressions API test to pass email as an object --- src/__tests__/lib/api/resources/Suppressions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/lib/api/resources/Suppressions.test.ts b/src/__tests__/lib/api/resources/Suppressions.test.ts index 55951ce..071dabd 100644 --- a/src/__tests__/lib/api/resources/Suppressions.test.ts +++ b/src/__tests__/lib/api/resources/Suppressions.test.ts @@ -109,7 +109,7 @@ describe("lib/api/resources/Suppressions: ", () => { expect.assertions(3); mock.onGet(endpoint, { params: { email } }).reply(200, [mockSuppression]); - const result = await suppressionsAPI.getList(email); + const result = await suppressionsAPI.getList({ email }); expect(mock.history.get[0].url).toEqual(endpoint); expect(mock.history.get[0].params).toEqual({ email }); From 70541b58549295878140a90332255e3cee2478a4 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Thu, 3 Jul 2025 10:59:20 +0400 Subject: [PATCH 14/17] refactor: update suppressions flow to improve clarity and use object for email filtering --- examples/sending/suppressions.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/sending/suppressions.ts b/examples/sending/suppressions.ts index 010a24e..6b6ded6 100644 --- a/examples/sending/suppressions.ts +++ b/examples/sending/suppressions.ts @@ -9,17 +9,17 @@ const client = new MailtrapClient({ }); async function suppressionsFlow() { - // Get all suppressions - const allSuppressions = await client.suppressions.getList(); - console.log("All suppressions:", allSuppressions); + // Get suppressions (up to 1000 per request) + const suppressions = await client.suppressions.getList(); + console.log("Suppressions (up to 1000):", suppressions); // Get suppressions filtered by email - const filteredSuppressions = await client.suppressions.getList("test@example.com"); + const filteredSuppressions = await client.suppressions.getList({email: "test@example.com"}); console.log("Filtered suppressions:", filteredSuppressions); // Delete a suppression by ID (if any exist) - if (allSuppressions.length > 0) { - const suppressionToDelete = allSuppressions[0]; + if (suppressions.length > 0) { + const suppressionToDelete = suppressions[0]; await client.suppressions.delete(suppressionToDelete.id); console.log(`Suppression ${suppressionToDelete.id} deleted successfully`); } else { From 8c28b493de715cb741ba9fa28e5c92770f53d634 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Thu, 3 Jul 2025 10:59:41 +0400 Subject: [PATCH 15/17] test: enhance Suppressions API tests to specify error types and improve clarity --- .../lib/api/resources/Suppressions.test.ts | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/__tests__/lib/api/resources/Suppressions.test.ts b/src/__tests__/lib/api/resources/Suppressions.test.ts index 071dabd..2767133 100644 --- a/src/__tests__/lib/api/resources/Suppressions.test.ts +++ b/src/__tests__/lib/api/resources/Suppressions.test.ts @@ -90,7 +90,7 @@ describe("lib/api/resources/Suppressions: ", () => { }); describe("getList(): ", () => { - it("successfully gets all suppressions.", async () => { + it("successfully gets suppressions (up to 1000 per request).", async () => { const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`; expect.assertions(2); @@ -116,13 +116,31 @@ describe("lib/api/resources/Suppressions: ", () => { expect(result).toEqual([mockSuppression]); }); - it("fails with error.", async () => { + it("fails with unauthorized error (401).", async () => { const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`; - const expectedErrorMessage = "Request failed with status code 400"; + const expectedErrorMessage = "Incorrect API token"; expect.assertions(2); - mock.onGet(endpoint).reply(400, { error: expectedErrorMessage }); + mock.onGet(endpoint).reply(401, { error: expectedErrorMessage }); + + try { + await suppressionsAPI.getList(); + } catch (error) { + expect(error).toBeInstanceOf(MailtrapError); + if (error instanceof MailtrapError) { + expect(error.message).toEqual(expectedErrorMessage); + } + } + }); + + it("fails with forbidden error (403).", async () => { + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`; + const expectedErrorMessage = "Access forbidden"; + + expect.assertions(2); + + mock.onGet(endpoint).reply(403, { errors: expectedErrorMessage }); try { await suppressionsAPI.getList(); @@ -149,13 +167,31 @@ describe("lib/api/resources/Suppressions: ", () => { expect(mock.history.delete[0].url).toEqual(endpoint); }); - it("fails with error.", async () => { + it("fails with unauthorized error (401).", async () => { + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`; + const expectedErrorMessage = "Incorrect API token"; + + expect.assertions(2); + + mock.onDelete(endpoint).reply(401, { error: expectedErrorMessage }); + + try { + await suppressionsAPI.delete(suppressionId); + } catch (error) { + expect(error).toBeInstanceOf(MailtrapError); + if (error instanceof MailtrapError) { + expect(error.message).toEqual(expectedErrorMessage); + } + } + }); + + it("fails with forbidden error (403).", async () => { const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`; - const expectedErrorMessage = "Request failed with status code 404"; + const expectedErrorMessage = "Access forbidden"; expect.assertions(2); - mock.onDelete(endpoint).reply(404, { error: expectedErrorMessage }); + mock.onDelete(endpoint).reply(403, { errors: expectedErrorMessage }); try { await suppressionsAPI.delete(suppressionId); From 9312aa246ff53f69d7122732e07b4ab5f4801e93 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Fri, 4 Jul 2025 13:37:52 +0400 Subject: [PATCH 16/17] refactor: streamline suppressions getter by introducing account ID validation method --- src/lib/MailtrapClient.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/MailtrapClient.ts b/src/lib/MailtrapClient.ts index 42bd1cc..a41060e 100644 --- a/src/lib/MailtrapClient.ts +++ b/src/lib/MailtrapClient.ts @@ -151,9 +151,7 @@ export default class MailtrapClient { * Getter for Suppressions API. */ get suppressions() { - if (!this.accountId) { - throw new MailtrapError(ACCOUNT_ID_MISSING); - } + this.validateAccountIdPresence(); return new SuppressionsBaseAPI(this.axios, this.accountId); } From 3ba336517172f34da3ad0f9c0303b2544766b4f7 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Fri, 4 Jul 2025 13:38:58 +0400 Subject: [PATCH 17/17] fix: correct spelling of "initializes" in multiple test files --- src/__tests__/lib/api/ContactLists.test.ts | 2 +- src/__tests__/lib/api/Contacts.test.ts | 2 +- src/__tests__/lib/api/General.test.ts | 2 +- src/__tests__/lib/api/Suppressions.test.ts | 2 +- src/__tests__/lib/api/Templates.test.ts | 2 +- src/__tests__/lib/api/Testing.test.ts | 2 +- src/__tests__/lib/api/resources/AccountAccesses.test.ts | 2 +- src/__tests__/lib/api/resources/Accounts.test.ts | 2 +- src/__tests__/lib/api/resources/Attachments.test.ts | 2 +- src/__tests__/lib/api/resources/Inboxes.test.ts | 2 +- src/__tests__/lib/api/resources/Messages.test.ts | 2 +- src/__tests__/lib/api/resources/Permissions.test.ts | 2 +- src/__tests__/lib/api/resources/Projects.test.ts | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/__tests__/lib/api/ContactLists.test.ts b/src/__tests__/lib/api/ContactLists.test.ts index 3cd33e3..2831c86 100644 --- a/src/__tests__/lib/api/ContactLists.test.ts +++ b/src/__tests__/lib/api/ContactLists.test.ts @@ -8,7 +8,7 @@ describe("lib/api/ContactLists: ", () => { describe("class ContactLists(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(contactListsAPI).toHaveProperty("create"); expect(contactListsAPI).toHaveProperty("getList"); expect(contactListsAPI).toHaveProperty("get"); diff --git a/src/__tests__/lib/api/Contacts.test.ts b/src/__tests__/lib/api/Contacts.test.ts index 0640293..03d151a 100644 --- a/src/__tests__/lib/api/Contacts.test.ts +++ b/src/__tests__/lib/api/Contacts.test.ts @@ -8,7 +8,7 @@ describe("lib/api/Contacts: ", () => { describe("class Contacts(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(contactsAPI).toHaveProperty("create"); expect(contactsAPI).toHaveProperty("get"); expect(contactsAPI).toHaveProperty("update"); diff --git a/src/__tests__/lib/api/General.test.ts b/src/__tests__/lib/api/General.test.ts index 224fec1..2053c45 100644 --- a/src/__tests__/lib/api/General.test.ts +++ b/src/__tests__/lib/api/General.test.ts @@ -8,7 +8,7 @@ describe("lib/api/General: ", () => { describe("class General(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(generalAPI).toHaveProperty("accountAccesses"); expect(generalAPI).toHaveProperty("accounts"); expect(generalAPI).toHaveProperty("permissions"); diff --git a/src/__tests__/lib/api/Suppressions.test.ts b/src/__tests__/lib/api/Suppressions.test.ts index 920fa7a..1745862 100644 --- a/src/__tests__/lib/api/Suppressions.test.ts +++ b/src/__tests__/lib/api/Suppressions.test.ts @@ -8,7 +8,7 @@ describe("lib/api/Suppressions: ", () => { describe("class SuppressionsBaseAPI(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(suppressionsAPI).toHaveProperty("getList"); expect(suppressionsAPI).toHaveProperty("delete"); }); diff --git a/src/__tests__/lib/api/Templates.test.ts b/src/__tests__/lib/api/Templates.test.ts index 8b490e8..3c36140 100644 --- a/src/__tests__/lib/api/Templates.test.ts +++ b/src/__tests__/lib/api/Templates.test.ts @@ -8,7 +8,7 @@ describe("lib/api/Templates: ", () => { describe("class TemplatesBaseAPI(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(templatesAPI).toHaveProperty("create"); expect(templatesAPI).toHaveProperty("getList"); expect(templatesAPI).toHaveProperty("get"); diff --git a/src/__tests__/lib/api/Testing.test.ts b/src/__tests__/lib/api/Testing.test.ts index 0ecf24d..b77b35d 100644 --- a/src/__tests__/lib/api/Testing.test.ts +++ b/src/__tests__/lib/api/Testing.test.ts @@ -8,7 +8,7 @@ describe("lib/api/Testing: ", () => { describe("class Testing(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(testingAPI).toHaveProperty("projects"); expect(testingAPI).toHaveProperty("inboxes"); expect(testingAPI).toHaveProperty("messages"); diff --git a/src/__tests__/lib/api/resources/AccountAccesses.test.ts b/src/__tests__/lib/api/resources/AccountAccesses.test.ts index 156e58b..2e39390 100644 --- a/src/__tests__/lib/api/resources/AccountAccesses.test.ts +++ b/src/__tests__/lib/api/resources/AccountAccesses.test.ts @@ -42,7 +42,7 @@ describe("lib/api/resources/AccountAccesses: ", () => { describe("class AccountAccesses(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(accountAccessesAPI).toHaveProperty("listAccountAccesses"); expect(accountAccessesAPI).toHaveProperty("removeAccountAccess"); }); diff --git a/src/__tests__/lib/api/resources/Accounts.test.ts b/src/__tests__/lib/api/resources/Accounts.test.ts index 3a67ac5..0ed3920 100644 --- a/src/__tests__/lib/api/resources/Accounts.test.ts +++ b/src/__tests__/lib/api/resources/Accounts.test.ts @@ -22,7 +22,7 @@ describe("lib/api/resources/Accounts: ", () => { describe("class Accounts(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(accountsAPI).toHaveProperty("getAllAccounts"); }); }); diff --git a/src/__tests__/lib/api/resources/Attachments.test.ts b/src/__tests__/lib/api/resources/Attachments.test.ts index 508b8e9..21a696e 100644 --- a/src/__tests__/lib/api/resources/Attachments.test.ts +++ b/src/__tests__/lib/api/resources/Attachments.test.ts @@ -34,7 +34,7 @@ describe("lib/api/resources/Attachments: ", () => { describe("class Attachments(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(attachmentsAPI).toHaveProperty("get"); expect(attachmentsAPI).toHaveProperty("getList"); }); diff --git a/src/__tests__/lib/api/resources/Inboxes.test.ts b/src/__tests__/lib/api/resources/Inboxes.test.ts index cc19859..cc86e6a 100644 --- a/src/__tests__/lib/api/resources/Inboxes.test.ts +++ b/src/__tests__/lib/api/resources/Inboxes.test.ts @@ -42,7 +42,7 @@ describe("lib/api/resources/Inboxes: ", () => { describe("class Inboxes(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(inboxesAPI).toHaveProperty("cleanInbox"); expect(inboxesAPI).toHaveProperty("create"); expect(inboxesAPI).toHaveProperty("delete"); diff --git a/src/__tests__/lib/api/resources/Messages.test.ts b/src/__tests__/lib/api/resources/Messages.test.ts index a630f2a..8b7da8c 100644 --- a/src/__tests__/lib/api/resources/Messages.test.ts +++ b/src/__tests__/lib/api/resources/Messages.test.ts @@ -45,7 +45,7 @@ describe("lib/api/resources/Messages: ", () => { describe("class Messages(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(messagesAPI).toHaveProperty("deleteMessage"); expect(messagesAPI).toHaveProperty("forward"); expect(messagesAPI).toHaveProperty("get"); diff --git a/src/__tests__/lib/api/resources/Permissions.test.ts b/src/__tests__/lib/api/resources/Permissions.test.ts index 30344a8..3b50779 100644 --- a/src/__tests__/lib/api/resources/Permissions.test.ts +++ b/src/__tests__/lib/api/resources/Permissions.test.ts @@ -28,7 +28,7 @@ describe("lib/api/resources/Permissions: ", () => { describe("class Permissions(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(permissionsAPI).toHaveProperty("getResources"); expect(permissionsAPI).toHaveProperty("bulkPermissionsUpdate"); }); diff --git a/src/__tests__/lib/api/resources/Projects.test.ts b/src/__tests__/lib/api/resources/Projects.test.ts index cd4f554..fc96103 100644 --- a/src/__tests__/lib/api/resources/Projects.test.ts +++ b/src/__tests__/lib/api/resources/Projects.test.ts @@ -24,7 +24,7 @@ describe("lib/api/resources/Projects: ", () => { describe("class Projects(): ", () => { describe("init: ", () => { - it("initalizes with all necessary params.", () => { + it("initializes with all necessary params.", () => { expect(projectsAPI).toHaveProperty("create"); expect(projectsAPI).toHaveProperty("delete"); expect(projectsAPI).toHaveProperty("getById");