Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions inc/managers/class-domain-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,16 @@ public function handle_site_created($site): void {
'site_id' => $site->blog_id,
];

wu_enqueue_async_action('wu_add_subdomain', $args, 'domain');
/*
* Only enqueue the async action when at least one host-provider integration
* has hooked into wu_add_subdomain. Otherwise Action Scheduler runs the job
* with zero callbacks and logs "no callbacks are registered" on every site
* creation, which is noise — the wu_async_process_domain_stage chain handles
* DNS/SSL progression independently.
*/
if (has_action('wu_add_subdomain')) {
wu_enqueue_async_action('wu_add_subdomain', $args, 'domain');
}

// Create a domain record for the site
$this->create_domain_record_for_site($site);
Expand Down Expand Up @@ -764,7 +773,16 @@ public function send_domain_to_host($old_value, $new_value, $item_id): void {
'site_id' => $site_id,
];

wu_enqueue_async_action('wu_add_domain', $args, 'domain');
/*
* Only enqueue the async action when at least one host-provider
* integration has hooked into wu_add_domain. Otherwise Action Scheduler
* runs the job with zero callbacks and logs "no callbacks are
* registered" on every domain change — see handle_site_created() for
* the equivalent wu_add_subdomain guard.
*/
if (has_action('wu_add_domain')) {
wu_enqueue_async_action('wu_add_domain', $args, 'domain');
}

/**
* Fires when a custom domain is added to the network.
Expand Down
236 changes: 236 additions & 0 deletions tests/WP_Ultimo/Managers/Domain_Manager_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@
*/
public function test_manager_slug(): void {
$reflection = new \ReflectionClass($this->domain_manager);
$slug_prop = $reflection->getProperty('slug');

Check warning on line 327 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

if (PHP_VERSION_ID < 80100) {
$slug_prop->setAccessible(true);
Expand Down Expand Up @@ -843,7 +843,7 @@

wp_cache_flush();

$site = get_blog_details($blog_id);

Check warning on line 846 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space
$mappings = Domain::get_by_site($site);

$this->assertNotFalse($mappings);
Expand Down Expand Up @@ -1132,6 +1132,242 @@
$this->assertTrue(true); // If we got here, no exception was thrown
}

// ----------------------------------------------------------------
// NEW: wu_add_domain / wu_add_subdomain are skipped when no host
// provider integration is listening. Regression for the "no callbacks
// are registered" Action Scheduler warning logged on every site
// creation when no host integration is enabled.
// ----------------------------------------------------------------

/**
* Test send_domain_to_host does not enqueue wu_add_domain when no listener is hooked.
*/
public function test_send_domain_to_host_skips_enqueue_without_listener(): void {
$unique_domain = 'no-listener-' . uniqid('', true) . '.example.com';

// Stash any pre-existing listeners so this test can deterministically
// assert "nothing got enqueued" — the production guard must skip the
// async enqueue when no host-provider integration is listening.
$prior_callbacks = $GLOBALS['wp_filter']['wu_add_domain'] ?? null;
unset($GLOBALS['wp_filter']['wu_add_domain']);

try {
$domain = wu_create_domain([
'blog_id' => $this->get_blog_id(),
'domain' => $unique_domain,
'stage' => Domain_Stage::DONE,
]);

$this->assertNotWPError($domain);

$this->assertFalse(
has_action('wu_add_domain'),
'precondition: wu_add_domain must have no listeners'
);

$this->domain_manager->send_domain_to_host(
'old-' . $unique_domain,
$unique_domain,
$domain->get_id()
);

$enqueued_args = [
'domain' => $unique_domain,
'site_id' => $domain->get_site_id(),
];

$matching_actions = wu_get_scheduled_actions(
[
'hook' => 'wu_add_domain',
'status' => \ActionScheduler_Store::STATUS_PENDING,
'args' => $enqueued_args,
'per_page' => 5,
],
'ids'
);

$this->assertEmpty(
$matching_actions,
sprintf(
'wu_add_domain must not be enqueued for args %s; found action ids: %s',
var_export($enqueued_args, true),

Check warning on line 1193 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

var_export() found. Debug code should not normally be used in production.
var_export($matching_actions, true)

Check warning on line 1194 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

var_export() found. Debug code should not normally be used in production.
)
);
} finally {
if (null !== $prior_callbacks) {
$GLOBALS['wp_filter']['wu_add_domain'] = $prior_callbacks;
}
}
}

/**
* Test send_domain_to_host enqueues wu_add_domain when a listener is hooked.
*/
public function test_send_domain_to_host_enqueues_when_listener_registered(): void {
$callback = function () {
// no-op listener — its presence is what we are testing
};

$prior_callbacks = $GLOBALS['wp_filter']['wu_add_domain'] ?? null;
unset($GLOBALS['wp_filter']['wu_add_domain']);

try {
$domain = wu_create_domain([
'blog_id' => $this->get_blog_id(),
'domain' => 'with-listener-host.example.com',
'stage' => Domain_Stage::DONE,
]);

$this->assertNotWPError($domain);

wu_unschedule_all_actions('wu_add_domain');

add_action('wu_add_domain', $callback);

$this->domain_manager->send_domain_to_host(
'old-with-listener.example.com',
'with-listener-host.example.com',
$domain->get_id()
);

$enqueued_args = [
'domain' => 'with-listener-host.example.com',
'site_id' => $domain->get_site_id(),
];

$matching_actions = wu_get_scheduled_actions(
[
'hook' => 'wu_add_domain',
'status' => \ActionScheduler_Store::STATUS_PENDING,
'args' => $enqueued_args,
'per_page' => 5,
],
'ids'
);

$this->assertNotEmpty(
$matching_actions,
'wu_add_domain must be enqueued when a host-provider integration is listening.'
);
} finally {
remove_action('wu_add_domain', $callback);
if (null !== $prior_callbacks) {
$GLOBALS['wp_filter']['wu_add_domain'] = $prior_callbacks;
}
wu_unschedule_all_actions('wu_add_domain');
}
}

/**
* Test handle_site_created does not enqueue wu_add_subdomain when no listener is hooked.
*/
public function test_handle_site_created_skips_subdomain_enqueue_without_listener(): void {
global $current_site;

$unique_sub = 'no-listener-' . uniqid('', false) . '.' . $current_site->domain;

$prior_callbacks = $GLOBALS['wp_filter']['wu_add_subdomain'] ?? null;
unset($GLOBALS['wp_filter']['wu_add_subdomain']);

try {
$blog_id = $this->create_test_blog([
'domain' => $unique_sub,
]);

$site = get_blog_details($blog_id);

$this->assertFalse(
has_action('wu_add_subdomain'),
'precondition: wu_add_subdomain must have no listeners'
);

$this->domain_manager->handle_site_created($site);

$enqueued_args = [
'subdomain' => $site->domain,
'site_id' => $site->blog_id,
];

$matching_actions = wu_get_scheduled_actions(
[
'hook' => 'wu_add_subdomain',
'status' => \ActionScheduler_Store::STATUS_PENDING,
'args' => $enqueued_args,
'per_page' => 5,
],
'ids'
);

$this->assertEmpty(
$matching_actions,
sprintf(
'wu_add_subdomain must not be enqueued for args %s; found action ids: %s',
var_export($enqueued_args, true),

Check warning on line 1306 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

var_export() found. Debug code should not normally be used in production.
var_export($matching_actions, true)

Check warning on line 1307 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

var_export() found. Debug code should not normally be used in production.
)
);
} finally {
if (null !== $prior_callbacks) {
$GLOBALS['wp_filter']['wu_add_subdomain'] = $prior_callbacks;
}
}
}

/**
* Test handle_site_created enqueues wu_add_subdomain when a listener is hooked.
*/
public function test_handle_site_created_enqueues_subdomain_when_listener_registered(): void {
global $current_site;

$callback = function () {
// no-op listener — its presence is what we are testing
};

$prior_callbacks = $GLOBALS['wp_filter']['wu_add_subdomain'] ?? null;
unset($GLOBALS['wp_filter']['wu_add_subdomain']);

try {
$blog_id = $this->create_test_blog([
'domain' => 'with-listener-sub.' . $current_site->domain,
]);

$site = get_blog_details($blog_id);

wu_unschedule_all_actions('wu_add_subdomain');

add_action('wu_add_subdomain', $callback);

$this->domain_manager->handle_site_created($site);

$enqueued_args = [
'subdomain' => $site->domain,
'site_id' => $site->blog_id,
];

$matching_actions = wu_get_scheduled_actions(
[
'hook' => 'wu_add_subdomain',
'status' => \ActionScheduler_Store::STATUS_PENDING,
'args' => $enqueued_args,
'per_page' => 5,
],
'ids'
);

$this->assertNotEmpty(
$matching_actions,
'wu_add_subdomain must be enqueued when a host-provider integration is listening.'
);
} finally {
remove_action('wu_add_subdomain', $callback);
if (null !== $prior_callbacks) {
$GLOBALS['wp_filter']['wu_add_subdomain'] = $prior_callbacks;
}
wu_unschedule_all_actions('wu_add_subdomain');
}
}

// ----------------------------------------------------------------
// NEW: Domain set_domain normalizes to lowercase
// ----------------------------------------------------------------
Expand Down Expand Up @@ -1426,7 +1662,7 @@
*/
public function test_date_created(): void {
$domain = new Domain();
$date = '2025-01-01 12:00:00';

Check warning on line 1665 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space
$domain->set_date_created($date);

$this->assertEquals($date, $domain->get_date_created());
Expand Down Expand Up @@ -1554,7 +1790,7 @@
*/
public function test_domain_validation_rules(): void {
$domain = new Domain();
$rules = $domain->validation_rules();

Check warning on line 1793 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

$this->assertIsArray($rules);
$this->assertArrayHasKey('blog_id', $rules);
Expand Down Expand Up @@ -1615,7 +1851,7 @@

// Domain should still be in checking-dns stage (DNS won't resolve in test env)
$fetched = wu_get_domain($domain->get_id());
$stage = $fetched->get_stage();

Check warning on line 1854 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

// It should either still be checking-dns (retry scheduled) or failed (if tries exceeded)
$this->assertContains($stage, [Domain_Stage::CHECKING_DNS, Domain_Stage::FAILED]);
Expand Down Expand Up @@ -1839,7 +2075,7 @@
// The wu_async_remove_old_primary_domains action should have been called
// when domain2 is created as primary
$action_called = false;
add_action('wu_async_remove_old_primary_domains', function ($domains) use (&$action_called) {

Check warning on line 2078 in tests/WP_Ultimo/Managers/Domain_Manager_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

The method parameter $domains is never used
$action_called = true;
});

Expand Down
Loading