diff --git a/.env.dist.testing b/.env.dist.testing
index f61980a..f69b10f 100644
--- a/.env.dist.testing
+++ b/.env.dist.testing
@@ -12,3 +12,6 @@ CONVERTKIT_API_TAG_NAME_2="gravityforms-tag-1"
CONVERTKIT_API_TAG_ID_2="2907192"
CONVERTKIT_API_SUBSCRIBER_EMAIL="optin@n7studios.com"
CONVERTKIT_API_SUBSCRIBER_ID="1579118532"
+CONVERTKIT_API_EMAIL_TEMPLATE_ID="5215567"
+CONVERTKIT_API_POST_ID="3175837"
+CONVERTKIT_API_SNIPPET_ID="136038"
diff --git a/.env.example b/.env.example
index a995c61..a8ce412 100644
--- a/.env.example
+++ b/.env.example
@@ -24,3 +24,6 @@ CONVERTKIT_API_TAG_NAME_2="gravityforms-tag-1"
CONVERTKIT_API_TAG_ID_2="2907192"
CONVERTKIT_API_SUBSCRIBER_EMAIL="optin@n7studios.com"
CONVERTKIT_API_SUBSCRIBER_ID="1579118532"
+CONVERTKIT_API_EMAIL_TEMPLATE_ID="5215567"
+CONVERTKIT_API_POST_ID="3175837"
+CONVERTKIT_API_SNIPPET_ID="136038"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index d389cee..1c322ab 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -22,6 +22,7 @@ jobs:
# Defines PHP Versions matrix to run tests on
strategy:
fail-fast: false
+ max-parallel: 1
matrix:
php-versions: [ '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ]
diff --git a/phpcs.xml b/phpcs.xml
index 3b059b2..9a763a5 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -72,7 +72,7 @@
-
+
diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php
index 92c8abd..4ca2f85 100644
--- a/src/ConvertKit_API_Traits.php
+++ b/src/ConvertKit_API_Traits.php
@@ -415,6 +415,156 @@ public function get_sequences(
);
}
+ /**
+ * Create a sequence
+ *
+ * @param string $name The name of the sequence.
+ * @param string $email_address The sending email address to use. Uses the account's sending email address if not provided.
+ * @param integer $email_template_id Id of the email template to use.
+ * @param array $send_days The days of the week to send the sequence on. Must be one of: `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`, `sunday`.
+ * @param integer $send_hour The hour of the day to send the sequence at. Must be an integer between 0 and 23.
+ * @param string $time_zone The timezone to use for the sequence. Must be a valid IANA timezone string.
+ * @param boolean $active Use `true` to activate the sequence, `false` to deactivate it.
+ * @param boolean $repeat When `true`, subscribers can restart the sequence multiple times.
+ * @param boolean $hold When `true`, subscribers added via Visual Automations stay in the sequence after receiving the last email.
+ * @param array> $exclude_subscriber_sources The subscriber sources to exclude from the sequence. Uses the account's default exclude subscriber sources if not provided.
+ *
+ * @see https://developers.kit.com/api-reference/sequences/create-a-sequence
+ *
+ * @return mixed|object
+ */
+ public function create_sequence(
+ string $name,
+ string $email_address = '',
+ int $email_template_id = 0,
+ array $send_days = [],
+ int $send_hour = 0,
+ string $time_zone = '',
+ bool $active = true,
+ bool $repeat = false,
+ bool $hold = false,
+ array $exclude_subscriber_sources = []
+ ) {
+ $options = [
+ 'name' => $name,
+ 'email_address' => $email_address,
+ 'email_template_id' => $email_template_id,
+ 'send_hour' => $send_hour,
+ 'time_zone' => $time_zone,
+ 'active' => $active,
+ 'repeat' => $repeat,
+ 'hold' => $hold,
+ ];
+ if (count($send_days)) {
+ $options['send_days'] = $send_days;
+ }
+ if (count($exclude_subscriber_sources)) {
+ $options['exclude_subscriber_sources'] = $exclude_subscriber_sources;
+ }
+
+ // Iterate through options, removing blank entries.
+ foreach ($options as $key => $value) {
+ if (is_string($value) && strlen($value) === 0) {
+ unset($options[$key]);
+ }
+ }
+
+ // Send request.
+ return $this->post(
+ 'sequences',
+ $options
+ );
+ }
+
+ /**
+ * Get a sequence.
+ *
+ * @param integer $id Sequence ID.
+ *
+ * @see https://developers.kit.com/api-reference/sequences/get-a-sequence
+ *
+ * @return mixed|object
+ */
+ public function get_sequence(int $id)
+ {
+ return $this->get(sprintf('sequences/%s', $id));
+ }
+
+ /**
+ * Updates a sequence
+ *
+ * @param integer $sequence_id Sequence ID.
+ * @param string $name The name of the sequence.
+ * @param string $email_address The sending email address to use. Uses the account's sending email address if not provided.
+ * @param integer $email_template_id Id of the email template to use.
+ * @param array $send_days The days of the week to send the sequence on. Must be one of: `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`, `sunday`.
+ * @param integer $send_hour The hour of the day to send the sequence at. Must be an integer between 0 and 23.
+ * @param string $time_zone The timezone to use for the sequence. Must be a valid IANA timezone string.
+ * @param boolean $active Use `true` to activate the sequence, `false` to deactivate it.
+ * @param boolean $repeat When `true`, subscribers can restart the sequence multiple times.
+ * @param boolean $hold When `true`, subscribers added via Visual Automations stay in the sequence after receiving the last email.
+ * @param array> $exclude_subscriber_sources The subscriber sources to exclude from the sequence. Uses the account's default exclude subscriber sources if not provided.
+ *
+ * @see https://developers.kit.com/api-reference/sequences/create-a-sequence
+ *
+ * @return mixed|object
+ */
+ public function update_sequence(
+ int $sequence_id,
+ string $name = '',
+ string $email_address = '',
+ int $email_template_id = 0,
+ array $send_days = [],
+ int $send_hour = 0,
+ string $time_zone = '',
+ bool $active = true,
+ bool $repeat = false,
+ bool $hold = false,
+ array $exclude_subscriber_sources = []
+ ) {
+ $options = [
+ 'name' => $name,
+ 'email_address' => $email_address,
+ 'email_template_id' => $email_template_id,
+ 'send_days' => $send_days,
+ 'send_hour' => $send_hour,
+ 'time_zone' => $time_zone,
+ 'active' => $active,
+ 'repeat' => $repeat,
+ 'hold' => $hold,
+ ];
+ if (count($exclude_subscriber_sources)) {
+ $options['exclude_subscriber_sources'] = $exclude_subscriber_sources;
+ }
+
+ // Iterate through options, removing blank entries.
+ foreach ($options as $key => $value) {
+ if (is_string($value) && strlen($value) === 0) {
+ unset($options[$key]);
+ }
+ }
+
+ // Send request.
+ return $this->put(
+ sprintf('sequences/%s', $sequence_id),
+ $options
+ );
+ }
+
+ /**
+ * Deletes a sequence.
+ *
+ * @param integer $id Sequence ID.
+ *
+ * @see https://developers.kit.com/api-reference/sequences/delete-a-sequence
+ *
+ * @return mixed|object
+ */
+ public function delete_sequence(int $id)
+ {
+ return $this->delete(sprintf('sequences/%s', $id));
+ }
+
/**
* Adds subscriber to sequence by email address
*
@@ -512,6 +662,343 @@ public function get_sequence_subscriptions(
);
}
+ /**
+ * List sequence emails
+ *
+ * @param integer $sequence_id Sequence ID.
+ * @param boolean $include_total_count To include the total count of records in the response, use true.
+ * @param string $after_cursor Return results after the given pagination cursor.
+ * @param string $before_cursor Return results before the given pagination cursor.
+ * @param integer $per_page Number of results to return.
+ *
+ * @see https://developers.kit.com/api-reference/sequence-emails/list-sequence-emails
+ *
+ * @return false|mixed
+ */
+ public function get_sequence_emails(
+ int $sequence_id,
+ bool $include_total_count = false,
+ string $after_cursor = '',
+ string $before_cursor = '',
+ int $per_page = 100
+ ) {
+ return $this->get(
+ sprintf('sequences/%s/emails', $sequence_id),
+ $this->build_total_count_and_pagination_params(
+ [],
+ $include_total_count,
+ $after_cursor,
+ $before_cursor,
+ $per_page
+ )
+ );
+ }
+
+ /**
+ * Create a sequence email
+ *
+ * @param integer $sequence_id Sequence ID.
+ * @param string $subject Subject line of the email.
+ * @param integer $delay_value Number of days or hours to wait before sending this email after the previous one.
+ * @param string $delay_unit Unit for the send delay. Use `days` for schedule-aware delivery, `hours` for a fixed hourly delay.
+ * @param string|null $preview_text Preview text shown in email clients before the email is opened.
+ * @param string|null $content HTML body content of the email.
+ * @param integer|null $email_template_id ID of the email template to use for layout and styling.
+ * @param boolean $published Whether the email is active and will be sent to subscribers.
+ * @param array|null $send_days Days of the week this email may be sent. Defaults to all 7 days (inherits the sequence schedule). Pass a subset to restrict delivery, or null to reset to all days.
+ * @param integer|null $position Zero-based position of the email in the sequence. Assigned automatically after the last email if omitted.
+ *
+ * @see https://developers.kit.com/api-reference/sequence-emails/create-a-sequence-email
+ *
+ * @return mixed|object
+ */
+ public function create_sequence_email(
+ int $sequence_id,
+ string $subject,
+ int $delay_value,
+ string $delay_unit,
+ string|null $preview_text = null,
+ string|null $content = null,
+ int|null $email_template_id = null,
+ bool $published = false,
+ array|null $send_days = null,
+ int|null $position = null,
+ ) {
+ $options = [
+ 'subject' => $subject,
+ 'delay_value' => $delay_value,
+ 'delay_unit' => $delay_unit,
+ 'published' => $published,
+ 'send_days' => $send_days,
+ ];
+
+ if (!empty($preview_text)) {
+ $options['preview_text'] = $preview_text;
+ }
+ if (!empty($content)) {
+ $options['content'] = $content;
+ }
+ if (!empty($email_template_id)) {
+ $options['email_template_id'] = $email_template_id;
+ }
+ if (!empty($position)) {
+ $options['position'] = $position;
+ }
+
+ // Send request.
+ return $this->post(
+ sprintf('sequences/%s/emails', $sequence_id),
+ $options
+ );
+ }
+
+ /**
+ * Get a sequence email.
+ *
+ * @param integer $sequence_id Sequence ID.
+ * @param integer $email_id Email ID.
+ *
+ * @see https://developers.kit.com/api-reference/sequence-emails/get-a-sequence-email
+ *
+ * @return mixed|object
+ */
+ public function get_sequence_email(int $sequence_id, int $email_id)
+ {
+ return $this->get(sprintf('sequences/%s/emails/%s', $sequence_id, $email_id));
+ }
+
+ /**
+ * Updates a sequence
+ *
+ * @param integer $sequence_id Sequence ID.
+ * @param integer $email_id Sequence Email ID.
+ * @param string|null $subject Subject line of the email.
+ * @param integer|null $delay_value Number of days or hours to wait before sending this email after the previous one.
+ * @param string|null $delay_unit Unit for the send delay. Use `days` for schedule-aware delivery, `hours` for a fixed hourly delay.
+ * @param string|null $preview_text Preview text shown in email clients before the email is opened.
+ * @param string|null $content HTML body content of the email.
+ * @param integer|null $email_template_id ID of the email template to use for layout and styling.
+ * @param boolean|null $published Whether the email is active and will be sent to subscribers.
+ * @param array|null $send_days Days of the week this email may be sent. Defaults to all 7 days (inherits the sequence schedule). Pass a subset to restrict delivery, or null to reset to all days.
+ * @param integer|null $position Zero-based position of the email in the sequence. Assigned automatically after the last email if omitted.
+ *
+ * @see https://developers.kit.com/api-reference/sequences/create-a-sequence
+ *
+ * @return mixed|object
+ */
+ public function update_sequence_email(
+ int $sequence_id,
+ int $email_id,
+ string|null $subject = null,
+ int|null $delay_value = null,
+ string|null $delay_unit = null,
+ string|null $preview_text = null,
+ string|null $content = null,
+ int|null $email_template_id = null,
+ bool|null $published = null,
+ array|null $send_days = null,
+ int|null $position = null,
+ ) {
+ // Build parameters.
+ $options = ['send_days' => $send_days];
+
+ if (!is_null($subject)) {
+ $options['subject'] = $subject;
+ }
+ if (!is_null($delay_value)) {
+ $options['delay_value'] = $delay_value;
+ }
+ if (!is_null($delay_unit)) {
+ $options['delay_unit'] = $delay_unit;
+ }
+ if (!is_null($preview_text)) {
+ $options['preview_text'] = $preview_text;
+ }
+ if (!is_null($content)) {
+ $options['content'] = $content;
+ }
+ if (!is_null($email_template_id)) {
+ $options['email_template_id'] = $email_template_id;
+ }
+ if (!is_null($published)) {
+ $options['published'] = $published;
+ }
+ if (!is_null($send_days)) {
+ $options['send_days'] = $send_days;
+ }
+ if (!is_null($position)) {
+ $options['position'] = $position;
+ }
+
+ // Send request.
+ return $this->put(
+ sprintf('sequences/%s/emails/%s', $sequence_id, $email_id),
+ $options
+ );
+ }
+
+ /**
+ * Deletes a sequence email.
+ *
+ * @param integer $sequence_id Sequence ID.
+ * @param integer $email_id Email ID.
+ *
+ * @see https://developers.kit.com/api-reference/sequence-emails/delete-a-sequence-email
+ *
+ * @return mixed|object
+ */
+ public function delete_sequence_email(int $sequence_id, int $email_id)
+ {
+ return $this->delete(sprintf('sequences/%s/emails/%s', $sequence_id, $email_id));
+ }
+
+ /**
+ * List snippets
+ *
+ * @param boolean $archived When `true`, returns only archived snippets. Defaults to `false`.
+ * @param boolean $include_content When `true`, includes both the content and document fields for each snippet in the response. Defaults to `false`.
+ * @param string|null $snippet_type Filter snippets by type. Use inline for text snippets or block for rich-text block snippets.
+ * @param boolean $include_total_count To include the total count of records in the response, use true.
+ * @param string $after_cursor Return results after the given pagination cursor.
+ * @param string $before_cursor Return results before the given pagination cursor.
+ * @param integer $per_page Number of results to return.
+ *
+ * @see https://developers.kit.com/api-reference/snippets/list-snippets
+ *
+ * @return false|mixed
+ */
+ public function get_snippets(
+ bool $archived = false,
+ bool $include_content = false,
+ string|null $snippet_type = null,
+ bool $include_total_count = false,
+ string $after_cursor = '',
+ string $before_cursor = '',
+ int $per_page = 100
+ ) {
+ $options = [
+ 'archived' => $archived,
+ 'include_content' => $include_content,
+ ];
+ if (!is_null($snippet_type)) {
+ $options['snippet_type'] = $snippet_type;
+ }
+ return $this->get(
+ 'snippets',
+ $this->build_total_count_and_pagination_params(
+ $options,
+ $include_total_count,
+ $after_cursor,
+ $before_cursor,
+ $per_page
+ )
+ );
+ }
+
+ /**
+ * Create a snippet
+ *
+ * @param string $name Name of the snippet.
+ * @param string $snippet_type Type of snippet. Must be one of: `inline`, `block`.
+ * @param string $content Content of the snippet.
+ *
+ * @see https://developers.kit.com/api-reference/snippets/create-a-snippet
+ *
+ * @return mixed|object
+ */
+ public function create_snippet(
+ string $name,
+ string $snippet_type,
+ string $content
+ ) {
+ $options = [
+ 'name' => $name,
+ 'snippet_type' => $snippet_type,
+ ];
+
+ switch ($snippet_type) {
+ case 'inline':
+ $options['content'] = $content;
+ break;
+
+ case 'block':
+ default:
+ $options['document_attributes'] = ['value_html' => $content];
+ break;
+ }
+
+ // Send request.
+ return $this->post(
+ 'snippets',
+ $options
+ );
+ }
+
+ /**
+ * Get a snippet.
+ *
+ * @param integer $id Snippet ID.
+ *
+ * @see https://developers.kit.com/api-reference/snippets/get-a-snippet
+ *
+ * @return mixed|object
+ */
+ public function get_snippet(int $id)
+ {
+ return $this->get(sprintf('snippets/%s', $id));
+ }
+
+ /**
+ * Updates a snippet
+ *
+ * @param integer $snippet_id Snippet ID.
+ * @param string $name Name of the snippet.
+ * @param string $snippet_type Type of snippet. Must be one of: `inline`, `block`.
+ * @param boolean $archived Pass `true` to archive or `false` to restore the snippet.
+ * @param string $content Content of the snippet.
+ *
+ * @see https://developers.kit.com/api-reference/snippets/update-a-snippet
+ *
+ * @return mixed|object
+ */
+ public function update_snippet(
+ int $snippet_id,
+ string $name = '',
+ string $snippet_type = '',
+ bool $archived = false,
+ string $content = ''
+ ) {
+ $options = [
+ 'name' => $name,
+ 'snippet_type' => $snippet_type,
+ 'archived' => $archived,
+ ];
+
+ switch ($snippet_type) {
+ case 'inline':
+ $options['content'] = $content;
+ break;
+
+ case 'block':
+ default:
+ $options['document_attributes'] = ['value_html' => $content];
+ break;
+ }
+
+ // Iterate through options, removing blank entries.
+ foreach ($options as $key => $value) {
+ if (is_string($value) && strlen($value) === 0) {
+ unset($options[$key]);
+ }
+ }
+
+ // Send request.
+ return $this->put(
+ sprintf('snippets/%s', $snippet_id),
+ $options
+ );
+ }
+
/**
* List tags.
*
@@ -811,6 +1298,57 @@ public function get_email_templates(
);
}
+ /**
+ * List posts.
+ *
+ * @param boolean $include_content To include the content field on each post in the response, use true.
+ * @param boolean $include_total_count To include the total count of records in the response, use true.
+ * @param string $after_cursor Return results after the given pagination cursor.
+ * @param string $before_cursor Return results before the given pagination cursor.
+ * @param integer $per_page Number of results to return.
+ *
+ * @since 2.5.0
+ *
+ * @see https://developers.kit.com/api-reference/posts/list-posts
+ *
+ * @return false|mixed
+ */
+ public function get_posts(
+ bool $include_content = false,
+ bool $include_total_count = false,
+ string $after_cursor = '',
+ string $before_cursor = '',
+ int $per_page = 100
+ ) {
+ // Send request.
+ return $this->get(
+ 'posts',
+ $this->build_total_count_and_pagination_params(
+ ['include_content' => $include_content],
+ $include_total_count,
+ $after_cursor,
+ $before_cursor,
+ $per_page
+ )
+ );
+ }
+
+ /**
+ * Get a post.
+ *
+ * @param integer $id Post ID.
+ *
+ * @since 2.5.0
+ *
+ * @see https://developers.kit.com/api-reference/posts/get-a-post
+ *
+ * @return mixed|object
+ */
+ public function get_post(int $id)
+ {
+ return $this->get(sprintf('posts/%s', $id));
+ }
+
/**
* List subscribers.
*
@@ -822,6 +1360,7 @@ public function get_email_templates(
* @param \DateTime|null $updated_before Filter subscribers who have been updated before this date.
* @param string $sort_field Sort Field (id|updated_at|cancelled_at).
* @param string $sort_order Sort Order (asc|desc).
+ * @param array $include Additional fields to include: attribution, tags, location, canceled_at.
* @param boolean $include_total_count To include the total count of records in the response, use true.
* @param string $after_cursor Return results after the given pagination cursor.
* @param string $before_cursor Return results before the given pagination cursor.
@@ -842,6 +1381,7 @@ public function get_subscribers(
\DateTime|null $updated_before = null,
string $sort_field = 'id',
string $sort_order = 'desc',
+ array $include = [],
bool $include_total_count = false,
string $after_cursor = '',
string $before_cursor = '',
@@ -874,6 +1414,9 @@ public function get_subscribers(
if (!empty($sort_order)) {
$options['sort_order'] = $sort_order;
}
+ if (!empty($include)) {
+ $options['include'] = implode(',', $include);
+ }
// Send request.
return $this->get(
@@ -2067,8 +2610,8 @@ public function post(string $endpoint, array $args = [])
/**
* Performs a PUT request to the API.
*
- * @param string $endpoint API Endpoint.
- * @param array|string> $args Request arguments.
+ * @param string $endpoint API Endpoint.
+ * @param array|boolean|integer|float|string>> $args Request arguments.
*
* @return false|mixed
*/
diff --git a/tests/ConvertKitAPITest.php b/tests/ConvertKitAPITest.php
index 8259adc..2620edb 100644
--- a/tests/ConvertKitAPITest.php
+++ b/tests/ConvertKitAPITest.php
@@ -1059,6 +1059,135 @@ public function testGetSequencesPagination()
$this->assertCount(1, $result->sequences);
}
+ /**
+ * Test that create_sequence(), update_sequence() and delete_sequence() works.
+ *
+ * We do all tests in a single function, so we don't end up with unnecessary
+ * Sequences remaining on the Kit account when running tests, which might impact
+ * other tests that expect (or do not expect) specific Sequences.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testCreateUpdateAndDeleteSequence()
+ {
+ // Create a sequence.
+ $result = $this->api->create_sequence(
+ name: 'Test Sequence',
+ email_address: 'wordpress@convertkit.com',
+ email_template_id: (int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'],
+ send_days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
+ send_hour: 12,
+ time_zone: 'America/Los_Angeles',
+ active: false,
+ repeat: false,
+ hold: false
+ );
+ $sequenceID = $result->sequence->id;
+
+ // Confirm the Sequence saved.
+ $result = get_object_vars($result->sequence);
+ $this->assertArrayHasKey('id', $result);
+ $this->assertEquals('Test Sequence', $result['name']);
+ $this->assertEquals('wordpress@convertkit.com', $result['email_address']);
+ $this->assertEquals((int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], $result['email_template_id']);
+ $this->assertEquals(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], $result['send_days']);
+ $this->assertEquals(12, $result['send_hour']);
+ $this->assertEquals('America/Los_Angeles', $result['time_zone']);
+ $this->assertEquals(false, $result['active']);
+ $this->assertEquals(false, $result['repeat']);
+ $this->assertEquals(false, $result['hold']);
+
+ // Update the existing sequence.
+ $result = $this->api->update_sequence(
+ sequence_id: $sequenceID,
+ name: 'Edited Test Sequence',
+ email_address: 'wordpress@convertkit.com',
+ email_template_id: (int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'],
+ send_days: ['saturday', 'sunday'],
+ send_hour: 13,
+ time_zone: 'America/New_York',
+ active: true,
+ repeat: true,
+ hold: true
+ );
+
+ // Confirm the changes saved.
+ $result = get_object_vars($result->sequence);
+ $this->assertArrayHasKey('id', $result);
+ $this->assertEquals('Edited Test Sequence', $result['name']);
+ $this->assertEquals('wordpress@convertkit.com', $result['email_address']);
+ $this->assertEquals((int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], $result['email_template_id']);
+ $this->assertEquals(['saturday', 'sunday'], $result['send_days']);
+ $this->assertEquals(13, $result['send_hour']);
+ $this->assertEquals('America/New_York', $result['time_zone']);
+ $this->assertEquals(true, $result['active']);
+ $this->assertEquals(true, $result['repeat']);
+ $this->assertEquals(true, $result['hold']);
+
+ // Delete Sequence.
+ $this->api->delete_sequence($sequenceID);
+ $this->assertEquals(204, $this->api->getResponseInterface()->getStatusCode());
+ }
+
+ /**
+ * Test that get_sequence() returns the expected data.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequence()
+ {
+ $result = $this->api->get_sequence((int) $_ENV['CONVERTKIT_API_SEQUENCE_ID']);
+ $this->assertInstanceOf('stdClass', $result);
+ $this->assertArrayHasKey('sequence', get_object_vars($result));
+ $this->assertArrayHasKey('id', get_object_vars($result->sequence));
+ }
+
+ /**
+ * Test that get_sequence() throws a ClientException when an invalid
+ * sequence ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequenceWithInvalidSequenceID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->get_sequence(12345);
+ }
+
+ /**
+ * Test that update_sequence() throws a ClientException when an invalid
+ * sequence ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testUpdateSequenceWithInvalidSequenceID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->update_sequence(12345);
+ }
+
+ /**
+ * Test that delete_sequence() throws a ClientException when an invalid
+ * sequence ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testDeleteSequenceWithInvalidSequenceID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->delete_sequence(12345);
+ }
+
/**
* Test that add_subscriber_to_sequence_by_email() returns the expected data.
*
@@ -1472,6 +1601,579 @@ public function testGetSequenceSubscriptionsWithInvalidPagination()
);
}
+ /**
+ * Test that get_sequence_emails() returns the expected data.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequenceEmails()
+ {
+ $result = $this->api->get_sequence_emails(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID']
+ );
+
+ // Assert emails and pagination exist.
+ $this->assertDataExists($result, 'emails');
+ $this->assertPaginationExists($result);
+
+ // Check first sequence in resultset has expected data.
+ $email = get_object_vars($result->emails[0]);
+ $this->assertArrayHasKey('id', $email);
+ $this->assertArrayHasKey('sequence_id', $email);
+ $this->assertArrayHasKey('subject', $email);
+ $this->assertArrayHasKey('preview_text', $email);
+ $this->assertArrayHasKey('email_address', $email);
+ $this->assertArrayHasKey('email_template_id', $email);
+ $this->assertArrayHasKey('published', $email);
+ $this->assertArrayHasKey('position', $email);
+ $this->assertArrayHasKey('delay_value', $email);
+ $this->assertArrayHasKey('delay_unit', $email);
+ $this->assertArrayHasKey('send_days', $email);
+ }
+
+ /**
+ * Test that get_sequence_emails() returns the expected data
+ * when the total count is included.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequenceEmailsWithTotalCount()
+ {
+ $result = $this->api->get_sequence_emails(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'],
+ include_total_count: true
+ );
+
+ // Assert sequences and pagination exist.
+ $this->assertDataExists($result, 'emails');
+ $this->assertPaginationExists($result);
+
+ // Assert total count is included.
+ $this->assertArrayHasKey('total_count', get_object_vars($result->pagination));
+ $this->assertGreaterThan(0, $result->pagination->total_count);
+ }
+
+ /**
+ * Test that get_sequence_emails() returns the expected data when
+ * pagination parameters and per_page limits are specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequenceEmailsPagination()
+ {
+ $result = $this->api->get_sequence_emails(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'],
+ per_page: 1
+ );
+
+ // Assert emails and pagination exist.
+ $this->assertDataExists($result, 'emails');
+ $this->assertPaginationExists($result);
+
+ // Assert a single email was returned.
+ $this->assertCount(1, $result->emails);
+
+ // Assert has_previous_page and has_next_page are correct.
+ $this->assertFalse($result->pagination->has_previous_page);
+ $this->assertTrue($result->pagination->has_next_page);
+
+ // Use pagination to fetch next page.
+ $result = $this->api->get_sequence_emails(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'],
+ per_page: 1,
+ after_cursor: $result->pagination->end_cursor
+ );
+
+ // Assert emails and pagination exist.
+ $this->assertDataExists($result, 'emails');
+ $this->assertPaginationExists($result);
+
+ // Assert a single email was returned.
+ $this->assertCount(1, $result->emails);
+
+ // Assert has_previous_page and has_next_page are correct.
+ $this->assertTrue($result->pagination->has_previous_page);
+ $this->assertFalse($result->pagination->has_next_page);
+
+ // Use pagination to fetch previous page.
+ $result = $this->api->get_sequence_emails(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'],
+ per_page: 1,
+ before_cursor: $result->pagination->start_cursor
+ );
+
+ // Assert emails and pagination exist.
+ $this->assertDataExists($result, 'emails');
+ $this->assertPaginationExists($result);
+
+ // Assert a single email was returned.
+ $this->assertCount(1, $result->emails);
+ }
+
+ /**
+ * Test that get_sequence_emails() throws a ClientException when an invalid
+ * sequence ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequenceEmailsWithInvalidSequenceID()
+ {
+ $this->expectException(ClientException::class);
+ $result = $this->api->get_sequence_emails(
+ sequence_id: 12345
+ );
+ }
+
+ /**
+ * Test that create_sequence_email(), get_sequence_email(), update_sequence_email()
+ * and delete_sequence_email() works.
+ *
+ * We do all tests in a single function, so we don't end up with unnecessary
+ * Sequence Emails remaining on the Kit account when running tests, which might impact
+ * other tests that expect (or do not expect) specific Sequence Emails.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testCreateGetUpdateAndDeleteSequenceEmail()
+ {
+ // Create a sequence email.
+ $result = $this->api->create_sequence_email(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'],
+ subject: 'Test Sequence Email',
+ delay_value: 1,
+ delay_unit: 'days',
+ preview_text: 'Test Preview Text',
+ content: 'Test Content',
+ email_template_id: (int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'],
+ published: false,
+ send_days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
+ position: 0
+ );
+ $sequenceEmailID = $result->email->id;
+
+ // Confirm the Sequence Email saved.
+ $result = get_object_vars($result->email);
+ $this->assertArrayHasKey('id', $result);
+ $this->assertEquals('Test Sequence Email', $result['subject']);
+ $this->assertEquals(1, $result['delay_value']);
+ $this->assertEquals('days', $result['delay_unit']);
+ $this->assertEquals('Test Preview Text', $result['preview_text']);
+ $this->assertEquals('Test Content', $result['content']);
+ $this->assertEquals((int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], $result['email_template_id']);
+ $this->assertEquals(false, $result['published']);
+ $this->assertEquals(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], $result['send_days']);
+ $this->assertEquals(2, $result['position']);
+
+ // Get the sequence email.
+ $result = $this->api->get_sequence_email(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'],
+ email_id: $sequenceEmailID
+ );
+
+ // Update the existing sequence email.
+ $result = $this->api->update_sequence_email(
+ sequence_id: (int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'],
+ email_id: $sequenceEmailID,
+ subject: 'Edited Test Sequence Email',
+ preview_text: 'Edited Test Preview Text',
+ content: 'Edited Test Content',
+ delay_value: 2,
+ delay_unit: 'hours',
+ email_template_id: (int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'],
+ published: true,
+ send_days: ['saturday', 'sunday'],
+ position: 1,
+ );
+
+ // Confirm the changes saved.
+ $result = get_object_vars($result->email);
+ $this->assertArrayHasKey('id', $result);
+ $this->assertEquals('Edited Test Sequence Email', $result['subject']);
+ $this->assertEquals(2, $result['delay_value']);
+ $this->assertEquals('hours', $result['delay_unit']);
+ $this->assertEquals('Edited Test Preview Text', $result['preview_text']);
+ $this->assertEquals('Edited Test Content', $result['content']);
+ $this->assertEquals((int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], $result['email_template_id']);
+ $this->assertEquals(true, $result['published']);
+ $this->assertEquals(['saturday', 'sunday'], $result['send_days']);
+ $this->assertEquals(1, $result['position']);
+
+ // Delete Sequence Email.
+ $this->api->delete_sequence_email((int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'], $sequenceEmailID);
+ $this->assertEquals(204, $this->api->getResponseInterface()->getStatusCode());
+ }
+
+ /**
+ * Test that get_sequence_email() throws a ClientException when an invalid
+ * sequence ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequenceEmailWithInvalidSequenceID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->get_sequence_email(12345, 12345);
+ }
+
+ /**
+ * Test that get_sequence_email() throws a ClientException when an invalid
+ * email ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSequenceEmailWithInvalidEmailID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->get_sequence_email((int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'], 12345);
+ }
+
+ /**
+ * Test that update_sequence_email() throws a ClientException when an invalid
+ * sequence email ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testUpdateSequenceEmailWithInvalidSequenceID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->update_sequence_email(12345, 12345);
+ }
+
+ /**
+ * Test that update_sequence_email() throws a ClientException when an invalid
+ * email ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testUpdateSequenceEmailWithInvalidEmailID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->update_sequence_email((int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'], 12345);
+ }
+
+ /**
+ * Test that delete_sequence_email() throws a ClientException when an invalid
+ * sequence email ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testDeleteSequenceEmailWithInvalidSequenceID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->delete_sequence_email(12345, 12345);
+ }
+
+ /**
+ * Test that delete_sequence_email() throws a ClientException when an invalid
+ * email ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testDeleteSequenceEmailWithInvalidEmailID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->delete_sequence_email((int) $_ENV['CONVERTKIT_API_SEQUENCE_ID'], 12345);
+ }
+
+ /**
+ * Test that get_snippets() returns the expected data.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSnippets()
+ {
+ $result = $this->api->get_snippets();
+
+ // Assert snippets and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Check first snippet in resultset has expected data.
+ $snippet = get_object_vars($result->snippets[0]);
+ $this->assertArrayHasKey('id', $snippet);
+ $this->assertArrayHasKey('name', $snippet);
+ $this->assertArrayHasKey('snippet_type', $snippet);
+ $this->assertArrayHasKey('archived', $snippet);
+ $this->assertArrayHasKey('key', $snippet);
+ $this->assertArrayHasKey('created_at', $snippet);
+ $this->assertArrayHasKey('updated_at', $snippet);
+ }
+
+ /**
+ * Test that get_snippets() returns the expected data when
+ * the snippet type is inline.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetInlineSnippets()
+ {
+ $result = $this->api->get_snippets(
+ snippet_type: 'inline'
+ );
+
+ // Assert snippets and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Assert snippets were returned.
+ $this->assertGreaterThan(0, count($result->snippets));
+ }
+
+ /**
+ * Test that get_snippets() returns the expected data when
+ * the snippet type is block.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetBlockSnippets()
+ {
+ $result = $this->api->get_snippets(
+ snippet_type: 'block'
+ );
+
+ // Assert snippets and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Assert no snippets were returned.
+ $this->assertCount(0, $result->snippets);
+ }
+
+ /**
+ * Test that get_snippets() returns the expected data when
+ * the archived parameter is used.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSnippetsWithArchivedParam()
+ {
+ $result = $this->api->get_snippets(
+ archived: true
+ );
+
+ // Assert snippets and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Assert snippets were returned.
+ $this->assertGreaterThan(0, count($result->snippets));
+ }
+
+ /**
+ * Test that get_snippets() returns the expected data
+ * when the total count is included.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSnippetsWithTotalCount()
+ {
+ $result = $this->api->get_snippets(
+ include_total_count: true
+ );
+
+ // Assert sequences and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Assert total count is included.
+ $this->assertArrayHasKey('total_count', get_object_vars($result->pagination));
+ $this->assertGreaterThan(0, $result->pagination->total_count);
+ }
+
+ /**
+ * Test that get_snippets() returns the expected data when
+ * pagination parameters and per_page limits are specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSnippetsPagination()
+ {
+ $result = $this->api->get_snippets(
+ per_page: 1
+ );
+
+ // Assert sequences and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Assert a single sequence was returned.
+ $this->assertCount(1, $result->snippets);
+
+ // Assert has_previous_page and has_next_page are correct.
+ $this->assertFalse($result->pagination->has_previous_page);
+ $this->assertTrue($result->pagination->has_next_page);
+
+ // Use pagination to fetch next page.
+ $result = $this->api->get_snippets(
+ per_page: 1,
+ after_cursor: $result->pagination->end_cursor
+ );
+
+ // Assert sequences and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Assert a single sequence was returned.
+ $this->assertCount(1, $result->snippets);
+
+ // Assert has_previous_page and has_next_page are correct.
+ $this->assertTrue($result->pagination->has_previous_page);
+ $this->assertFalse($result->pagination->has_next_page);
+
+ // Use pagination to fetch previous page.
+ $result = $this->api->get_snippets(
+ per_page: 1,
+ before_cursor: $result->pagination->start_cursor
+ );
+
+ // Assert sequences and pagination exist.
+ $this->assertDataExists($result, 'snippets');
+ $this->assertPaginationExists($result);
+
+ // Assert a single sequence was returned.
+ $this->assertCount(1, $result->snippets);
+ }
+
+ /**
+ * Test that create_snippet() works.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testCreateSnippet()
+ {
+ // Add mock handler for this API request, as the API doesn't provide
+ // a method to delete snippets to cleanup the test.
+ $this->api = $this->mockResponse(
+ api: $this->api,
+ responseBody: [
+ 'snippet' => [
+ 'id' => 12345,
+ 'name' => 'Test Snippet',
+ 'snippet_type' => 'inline',
+ 'content' => 'Test Content',
+ ],
+ ]
+ );
+
+ // Create a snippet.
+ $result = $this->api->create_snippet(
+ name: 'Test Snippet',
+ snippet_type: 'inline',
+ content: 'Test Content'
+ );
+ $snippetID = $result->snippet->id;
+
+ // Confirm the Snippet saved.
+ $result = get_object_vars($result->snippet);
+ $this->assertArrayHasKey('id', $result);
+ $this->assertEquals('Test Snippet', $result['name']);
+ $this->assertEquals('inline', $result['snippet_type']);
+ $this->assertEquals('Test Content', $result['content']);
+ }
+
+ /**
+ * Test that update_snippet() works.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testUpdateSnippet()
+ {
+ $result = $this->api->update_snippet(
+ snippet_id: (int) $_ENV['CONVERTKIT_API_SNIPPET_ID'],
+ name: 'Edited Test Snippet',
+ snippet_type: 'inline',
+ content: 'Edited Test Content'
+ );
+
+ // Confirm the changes saved.
+ $result = get_object_vars($result->snippet);
+ $this->assertArrayHasKey('id', $result);
+ $this->assertEquals('Edited Test Snippet', $result['name']);
+ $this->assertEquals('inline', $result['snippet_type']);
+ $this->assertEquals('Edited Test Content', $result['content']);
+ }
+
+ /**
+ * Test that get_snippet() returns the expected data.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSnippet()
+ {
+ $result = $this->api->get_snippet((int) $_ENV['CONVERTKIT_API_SNIPPET_ID']);
+ $this->assertInstanceOf('stdClass', $result);
+ $this->assertArrayHasKey('snippet', get_object_vars($result));
+ $this->assertArrayHasKey('id', get_object_vars($result->snippet));
+ }
+
+ /**
+ * Test that get_snippet() throws a ClientException when an invalid
+ * snippet ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSnippetWithInvalidSnippetID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->get_snippet(12345);
+ }
+
+ /**
+ * Test that update_snippet() throws a ClientException when an invalid
+ * snippet ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testUpdateSnippetWithInvalidSnippetID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->update_snippet(12345);
+ }
+
/**
* Test that get_tags() returns the expected data.
*
@@ -3315,6 +4017,28 @@ public function testGetSubscribersWithSortOrderParam()
);
}
+ /**
+ * Test that get_subscribers() returns the expected data
+ * when the include parameter is used.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetSubscribersWithIncludeParam()
+ {
+ $result = $this->api->get_subscribers(
+ include: ['tags']
+ );
+
+ // Assert subscribers and pagination exist.
+ $this->assertDataExists($result, 'subscribers');
+ $this->assertPaginationExists($result);
+
+ // Assert fields are included.
+ $this->assertArrayHasKey('tags', get_object_vars($result->subscribers[0]));
+ }
+
/**
* Test that get_subscribers() returns the expected data
* when pagination parameters and per_page limits are specified.
@@ -4344,6 +5068,159 @@ public function testGetEmailTemplatesPagination()
$this->assertTrue($result->pagination->has_next_page);
}
+ /**
+ * Test that get_posts() returns the expected data.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetPosts()
+ {
+ $result = $this->api->get_posts();
+
+ // Assert posts and pagination exist.
+ $this->assertDataExists($result, 'posts');
+ $this->assertPaginationExists($result);
+
+ // Assert content is not included.
+ $this->assertArrayNotHasKey('content', get_object_vars($result->posts[0]));
+ }
+
+ /**
+ * Test that get_posts() returns the expected data
+ * when the post content is included.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetPostsWithIncludeContent()
+ {
+ $result = $this->api->get_posts(
+ include_content: true,
+ per_page: 1
+ );
+
+ // Assert posts and pagination exist.
+ $this->assertDataExists($result, 'posts');
+ $this->assertPaginationExists($result);
+
+ // Assert content is included.
+ $this->assertArrayHasKey('content', get_object_vars($result->posts[0]));
+ }
+
+ /**
+ * Test that get_posts() returns the expected data
+ * when the total count is included.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetPostsWithTotalCount()
+ {
+ $result = $this->api->get_posts(
+ include_total_count: true
+ );
+
+ // Assert posts and pagination exist.
+ $this->assertDataExists($result, 'posts');
+ $this->assertPaginationExists($result);
+
+ // Assert total count is included.
+ $this->assertArrayHasKey('total_count', get_object_vars($result->pagination));
+ $this->assertGreaterThan(0, $result->pagination->total_count);
+ }
+
+ /**
+ * Test that get_posts() returns the expected data
+ * when pagination parameters and per_page limits are specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetPostsPagination()
+ {
+ $result = $this->api->get_posts(
+ per_page: 1
+ );
+
+ // Assert posts and pagination exist.
+ $this->assertDataExists($result, 'posts');
+ $this->assertPaginationExists($result);
+
+ // Assert a single post was returned.
+ $this->assertCount(1, $result->posts);
+
+ // Assert has_previous_page and has_next_page are correct.
+ $this->assertFalse($result->pagination->has_previous_page);
+ $this->assertTrue($result->pagination->has_next_page);
+
+ // Use pagination to fetch next page.
+ $result = $this->api->get_posts(
+ per_page: 1,
+ after_cursor: $result->pagination->end_cursor
+ );
+
+ // Assert posts and pagination exist.
+ $this->assertDataExists($result, 'posts');
+ $this->assertPaginationExists($result);
+
+ // Assert a single post was returned.
+ $this->assertCount(1, $result->posts);
+
+ // Assert has_previous_page and has_next_page are correct.
+ $this->assertTrue($result->pagination->has_previous_page);
+ $this->assertTrue($result->pagination->has_next_page);
+
+ // Use pagination to fetch previous page.
+ $result = $this->api->get_posts(
+ per_page: 1,
+ before_cursor: $result->pagination->start_cursor
+ );
+
+ // Assert posts and pagination exist.
+ $this->assertDataExists($result, 'posts');
+ $this->assertPaginationExists($result);
+
+ // Assert a single post was returned.
+ $this->assertCount(1, $result->posts);
+
+ // Assert has_previous_page and has_next_page are correct.
+ $this->assertFalse($result->pagination->has_previous_page);
+ $this->assertTrue($result->pagination->has_next_page);
+ }
+
+ /**
+ * Test that get_post() returns the expected data.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetPost()
+ {
+ $result = $this->api->get_post($_ENV['CONVERTKIT_API_POST_ID']);
+ $result = get_object_vars($result->post);
+ $this->assertEquals($result['id'], $_ENV['CONVERTKIT_API_POST_ID']);
+ }
+
+ /**
+ * Test that get_post() throws a ClientException when an invalid
+ * post ID is specified.
+ *
+ * @since 2.5.0
+ *
+ * @return void
+ */
+ public function testGetPostWithInvalidPostID()
+ {
+ $this->expectException(ClientException::class);
+ $this->api->get_post(12345);
+ }
+
/**
* Test that get_broadcasts() returns the expected data
* when a valid sent_after date is specified.