diff --git a/inc/Abilities/Email/EmailAbilities.php b/inc/Abilities/Email/EmailAbilities.php index 3e790da80..2e914f622 100644 --- a/inc/Abilities/Email/EmailAbilities.php +++ b/inc/Abilities/Email/EmailAbilities.php @@ -1334,7 +1334,7 @@ private function saveToSentFolder( array $to, string $subject, string $body, arr * * @param string $from The derived From address. */ - $from = apply_filters( 'dm_email_sent_folder_from', $from ); + $from = apply_filters( 'datamachine_email_sent_folder_from', $from ); $to_str = implode( ', ', $to ); $date = gmdate( 'r' ); diff --git a/inc/Abilities/Engine/RunFlowAbility.php b/inc/Abilities/Engine/RunFlowAbility.php index 090e6e15e..e19943221 100644 --- a/inc/Abilities/Engine/RunFlowAbility.php +++ b/inc/Abilities/Engine/RunFlowAbility.php @@ -502,7 +502,7 @@ private static function backpressureDeferSeconds( int $flow_id ): int { // Deterministic jitter in [0, base) spreads deferred flows across the // window instead of waking them all at base seconds. - $jitter = absint( crc32( 'dm_backpressure_' . $flow_id ) ) % $base; + $jitter = absint( crc32( 'datamachine_backpressure_' . $flow_id ) ) % $base; return $base + $jitter; } diff --git a/inc/Abilities/Flow/WebhookTriggerAbility.php b/inc/Abilities/Flow/WebhookTriggerAbility.php index b4c7a912a..c28089765 100644 --- a/inc/Abilities/Flow/WebhookTriggerAbility.php +++ b/inc/Abilities/Flow/WebhookTriggerAbility.php @@ -878,7 +878,7 @@ public function executeSetRateLimit( array $input ): array { } // Clear any existing rate limit counter so new config takes effect immediately. - delete_transient( 'dm_webhook_rate_' . $flow_id ); + delete_transient( \DataMachine\Api\WebhookTrigger::rate_limit_transient_key( $flow_id ) ); $effective_max = $rate_config['max'] ?? \DataMachine\Api\WebhookTrigger::DEFAULT_RATE_LIMIT_MAX; $effective_window = $rate_config['window'] ?? \DataMachine\Api\WebhookTrigger::DEFAULT_RATE_LIMIT_WINDOW; diff --git a/inc/Abilities/Media/GDRenderer.php b/inc/Abilities/Media/GDRenderer.php index 1d0bdc901..2fe58f855 100644 --- a/inc/Abilities/Media/GDRenderer.php +++ b/inc/Abilities/Media/GDRenderer.php @@ -787,7 +787,7 @@ public function save_temp( string $format = 'png', int $quality = -1 ): ?string return null; } - $temp_file = tempnam( sys_get_temp_dir(), 'dm_img_' ); + $temp_file = tempnam( sys_get_temp_dir(), 'datamachine_img_' ); $ext = 'jpeg' === $format ? '.jpg' : '.png'; $path = $temp_file . $ext; diff --git a/inc/Api/WebhookTrigger.php b/inc/Api/WebhookTrigger.php index 62372dc08..7138ed71b 100644 --- a/inc/Api/WebhookTrigger.php +++ b/inc/Api/WebhookTrigger.php @@ -518,7 +518,7 @@ private static function check_rate_limit( int $flow_id, array $scheduling_config return null; } - $transient_key = 'dm_webhook_rate_' . $flow_id; + $transient_key = self::rate_limit_transient_key( $flow_id ); $current_count = (int) get_transient( $transient_key ); if ( $current_count >= $max ) { @@ -568,6 +568,16 @@ function ( $result ) use ( $window ) { return null; } + /** + * Build the per-flow rate-limit transient key. + * + * @param int $flow_id Flow ID. + * @return string Transient key. + */ + public static function rate_limit_transient_key( int $flow_id ): string { + return 'datamachine_webhook_rate_' . $flow_id; + } + /** * Extract Bearer token from Authorization header. * diff --git a/inc/Engine/Tasks/RecurringScheduler.php b/inc/Engine/Tasks/RecurringScheduler.php index 57c441c50..c10cef8da 100644 --- a/inc/Engine/Tasks/RecurringScheduler.php +++ b/inc/Engine/Tasks/RecurringScheduler.php @@ -527,7 +527,7 @@ public static function calculateStaggerOffset( int $seed, int $interval_seconds if ( $max_offset <= 0 ) { return 0; } - return absint( crc32( 'dm_stagger_' . $seed ) ) % $max_offset; + return absint( crc32( 'datamachine_stagger_' . $seed ) ) % $max_offset; } /** diff --git a/inc/Engine/Tasks/TaskScheduler.php b/inc/Engine/Tasks/TaskScheduler.php index 5d2b592fd..38ba267b5 100644 --- a/inc/Engine/Tasks/TaskScheduler.php +++ b/inc/Engine/Tasks/TaskScheduler.php @@ -375,7 +375,7 @@ public static function scheduleBatch( string $taskType, array $itemParams, array // so the chain caller → batch_parent is preserved even though // per-item children chain off the caller directly (below). $jobs_db = new Jobs(); - $batch_id = 'dm_batch_' . wp_generate_uuid4(); + $batch_id = 'datamachine_batch_' . wp_generate_uuid4(); $batch_create_args = array( 'pipeline_id' => 'direct', 'flow_id' => 'direct', diff --git a/inc/migrations/scaffolding.php b/inc/migrations/scaffolding.php index 27e4ac281..476602a11 100644 --- a/inc/migrations/scaffolding.php +++ b/inc/migrations/scaffolding.php @@ -92,10 +92,10 @@ function datamachine_get_scaffold_defaults( string $agent_name = '' ): array { $admin_name = $admin_user ? $admin_user->display_name : ''; // --- Versions --- - $wp_version = get_bloginfo( 'version' ); - $php_version = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION; - $dm_version = defined( 'DATAMACHINE_VERSION' ) ? DATAMACHINE_VERSION : 'unknown'; - $created = wp_date( 'Y-m-d' ); + $wp_version = get_bloginfo( 'version' ); + $php_version = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION; + $datamachine_version = defined( 'DATAMACHINE_VERSION' ) ? DATAMACHINE_VERSION : 'unknown'; + $created = wp_date( 'Y-m-d' ); // --- Build SOUL.md context lines --- $context_items = array(); @@ -187,7 +187,7 @@ function datamachine_get_scaffold_defaults( string $agent_name = '' ): array { 'This file tracks persistent knowledge. Keep it lean — persistent facts only, not session logs.', '', '## State', - "- Data Machine v{$dm_version} activated on {$created}", + "- Data Machine v{$datamachine_version} activated on {$created}", "- WordPress {$wp_version}, PHP {$php_version}", '', '## Lessons Learned', diff --git a/tests/Unit/Abilities/Engine/PipelineBatchSchedulerTest.php b/tests/Unit/Abilities/Engine/PipelineBatchSchedulerTest.php index 14920dc21..4270d3d6d 100644 --- a/tests/Unit/Abilities/Engine/PipelineBatchSchedulerTest.php +++ b/tests/Unit/Abilities/Engine/PipelineBatchSchedulerTest.php @@ -165,7 +165,7 @@ public function test_fanout_stores_batch_state_in_engine_data(): void { $this->assertEquals( 'step_abc_123', $parent_engine['next_flow_step_id'] ); // No transient should exist. - $this->assertFalse( get_transient( 'dm_pipeline_batch_' . $parent_id ) ); + $this->assertFalse( get_transient( 'datamachine_pipeline_batch_' . $parent_id ) ); } /** diff --git a/tests/Unit/Abilities/TaxonomyAbilityWpErrorTest.php b/tests/Unit/Abilities/TaxonomyAbilityWpErrorTest.php index b193a89c0..75705d791 100644 --- a/tests/Unit/Abilities/TaxonomyAbilityWpErrorTest.php +++ b/tests/Unit/Abilities/TaxonomyAbilityWpErrorTest.php @@ -16,7 +16,7 @@ class TaxonomyAbilityWpErrorTest extends WP_UnitTestCase { - private const TEST_TAXONOMY = 'dm_wp_error_terms'; + private const TEST_TAXONOMY = 'datamachine_wp_error_terms'; public function set_up(): void { parent::set_up(); @@ -80,7 +80,7 @@ public function test_merge_term_meta_callback_failure_returns_wp_error(): void { 'term_id' => 0, 'taxonomy' => self::TEST_TAXONOMY, 'data' => array( 'name' => 'Charleston' ), - 'field_map' => array( 'name' => 'dm_name' ), + 'field_map' => array( 'name' => 'datamachine_name' ), ) ); @@ -94,7 +94,7 @@ public function test_merge_term_meta_static_helper_preserves_legacy_failure_arra 0, self::TEST_TAXONOMY, array( 'name' => 'Charleston' ), - array( 'name' => 'dm_name' ) + array( 'name' => 'datamachine_name' ) ); $this->assertIsArray( $result ); diff --git a/tests/Unit/Api/WebhookTriggerTest.php b/tests/Unit/Api/WebhookTriggerTest.php index f51b26831..a35780c54 100644 --- a/tests/Unit/Api/WebhookTriggerTest.php +++ b/tests/Unit/Api/WebhookTriggerTest.php @@ -44,7 +44,7 @@ public function set_up(): void { public function tear_down(): void { remove_all_filters( 'datamachine_webhook_auth_presets' ); - delete_transient( 'dm_webhook_rate_' . $this->flow_id ); + delete_transient( WebhookTrigger::rate_limit_transient_key( $this->flow_id ) ); parent::tear_down(); } diff --git a/tests/Unit/Core/WordPress/TaxonomyHandlerFiltersTest.php b/tests/Unit/Core/WordPress/TaxonomyHandlerFiltersTest.php index acbadc78f..1ebddd48f 100644 --- a/tests/Unit/Core/WordPress/TaxonomyHandlerFiltersTest.php +++ b/tests/Unit/Core/WordPress/TaxonomyHandlerFiltersTest.php @@ -28,7 +28,7 @@ class TaxonomyHandlerFiltersTest extends WP_UnitTestCase { * Intentionally NOT named after any real Extra Chill taxonomy so the * tests document the generic contract, not a vendor-specific shape. */ - private const TEST_TAXONOMY = 'dm_test_region'; + private const TEST_TAXONOMY = 'datamachine_test_region'; public function set_up(): void { parent::set_up(); diff --git a/tests/abilities-categories-load-order-smoke.php b/tests/abilities-categories-load-order-smoke.php index 92d34a029..4c6525a01 100644 --- a/tests/abilities-categories-load-order-smoke.php +++ b/tests/abilities-categories-load-order-smoke.php @@ -150,32 +150,32 @@ function __( $text, $domain = null ) { } if ( ! function_exists( 'doing_action' ) ) { function doing_action( $hook ) { - global $dm_2287_state; - return $dm_2287_state->doing && $hook === 'wp_abilities_api_categories_init'; + global $datamachine_2287_state; + return $datamachine_2287_state->doing && $hook === 'wp_abilities_api_categories_init'; } } if ( ! function_exists( 'did_action' ) ) { function did_action( $hook ) { - global $dm_2287_state; - return $hook === 'wp_abilities_api_categories_init' ? $dm_2287_state->did : 0; + global $datamachine_2287_state; + return $hook === 'wp_abilities_api_categories_init' ? $datamachine_2287_state->did : 0; } } if ( ! function_exists( 'add_action' ) ) { function add_action( $hook, $callback, $priority = 10, $accepted_args = 1 ) { - global $dm_2287_state; - $dm_2287_state->hooked[ $hook ][] = $callback; + global $datamachine_2287_state; + $datamachine_2287_state->hooked[ $hook ][] = $callback; return true; } } if ( ! function_exists( 'wp_register_ability_category' ) ) { function wp_register_ability_category( $slug, $args ) { - global $dm_2287_state; + global $datamachine_2287_state; // Only succeed when the action is firing (mirror core's guard). if ( ! doing_action( 'wp_abilities_api_categories_init' ) ) { - $dm_2287_state->doing_it_wrong = ( $dm_2287_state->doing_it_wrong ?? 0 ) + 1; + $datamachine_2287_state->doing_it_wrong = ( $datamachine_2287_state->doing_it_wrong ?? 0 ) + 1; return null; } - $dm_2287_state->registered[ $slug ] = $args; + $datamachine_2287_state->registered[ $slug ] = $args; return true; } } @@ -195,28 +195,28 @@ public static function get_instance(): self { return self::$instance; } public function is_registered( $slug ): bool { - global $dm_2287_state; - return isset( $dm_2287_state->registered[ $slug ] ); + global $datamachine_2287_state; + return isset( $datamachine_2287_state->registered[ $slug ] ); } public function register( $slug, $args ): bool { - global $dm_2287_state; - $dm_2287_state->registered[ $slug ] = $args; + global $datamachine_2287_state; + $datamachine_2287_state->registered[ $slug ] = $args; return true; } } } -$GLOBALS['dm_2287_state'] = $state; +$GLOBALS['datamachine_2287_state'] = $state; require_once $plugin_root . '/inc/Abilities/AbilityCategories.php'; $reset = static function (): void { - global $dm_2287_state; - $dm_2287_state->doing = false; - $dm_2287_state->did = 0; - $dm_2287_state->hooked = array(); - $dm_2287_state->registered = array(); - $dm_2287_state->doing_it_wrong = 0; + global $datamachine_2287_state; + $datamachine_2287_state->doing = false; + $datamachine_2287_state->did = 0; + $datamachine_2287_state->hooked = array(); + $datamachine_2287_state->registered = array(); + $datamachine_2287_state->doing_it_wrong = 0; $reflection = new ReflectionClass( \DataMachine\Abilities\AbilityCategories::class ); $prop = $reflection->getProperty( 'registered' ); diff --git a/tests/abilities-image-template-load-order-smoke.php b/tests/abilities-image-template-load-order-smoke.php index 337d7d810..46cda31e0 100644 --- a/tests/abilities-image-template-load-order-smoke.php +++ b/tests/abilities-image-template-load-order-smoke.php @@ -131,7 +131,7 @@ define( 'ABSPATH', '/tmp/' ); } -$GLOBALS['dm_2290_state'] = (object) array( +$GLOBALS['datamachine_2290_state'] = (object) array( 'doing' => false, 'did' => 0, 'hooked' => array(), @@ -147,35 +147,35 @@ function __( $text, $domain = null ) { if ( ! function_exists( 'doing_action' ) ) { function doing_action( $hook ) { - global $dm_2290_state; - return $dm_2290_state->doing && $hook === 'wp_abilities_api_init'; + global $datamachine_2290_state; + return $datamachine_2290_state->doing && $hook === 'wp_abilities_api_init'; } } if ( ! function_exists( 'did_action' ) ) { function did_action( $hook ) { - global $dm_2290_state; - return $hook === 'wp_abilities_api_init' ? $dm_2290_state->did : 0; + global $datamachine_2290_state; + return $hook === 'wp_abilities_api_init' ? $datamachine_2290_state->did : 0; } } if ( ! function_exists( 'add_action' ) ) { function add_action( $hook, $callback, $priority = 10, $accepted_args = 1 ) { - global $dm_2290_state; - $dm_2290_state->hooked[ $hook ][] = $callback; + global $datamachine_2290_state; + $datamachine_2290_state->hooked[ $hook ][] = $callback; return true; } } if ( ! function_exists( 'wp_register_ability' ) ) { function wp_register_ability( $name, $args ) { - global $dm_2290_state; + global $datamachine_2290_state; // Mirror core's guard: only succeed when doing_action(wp_abilities_api_init) is true. if ( ! doing_action( 'wp_abilities_api_init' ) ) { - ++$dm_2290_state->doing_it_wrong; + ++$datamachine_2290_state->doing_it_wrong; return null; } - $dm_2290_state->registered[ $name ] = $args; + $datamachine_2290_state->registered[ $name ] = $args; return true; } } @@ -193,12 +193,12 @@ public static function can_manage(): bool { return true; } require_once $plugin_root . '/inc/Abilities/Media/ImageTemplateAbilities.php'; $reset = static function (): void { - global $dm_2290_state; - $dm_2290_state->doing = false; - $dm_2290_state->did = 0; - $dm_2290_state->hooked = array(); - $dm_2290_state->registered = array(); - $dm_2290_state->doing_it_wrong = 0; + global $datamachine_2290_state; + $datamachine_2290_state->doing = false; + $datamachine_2290_state->did = 0; + $datamachine_2290_state->hooked = array(); + $datamachine_2290_state->registered = array(); + $datamachine_2290_state->doing_it_wrong = 0; $reflection = new ReflectionClass( \DataMachine\Abilities\Media\ImageTemplateAbilities::class ); $prop = $reflection->getProperty( 'registered' ); @@ -208,13 +208,13 @@ public static function can_manage(): bool { return true; } // --- State 1: doing_action — register immediately. $reset(); -$GLOBALS['dm_2290_state']->doing = true; +$GLOBALS['datamachine_2290_state']->doing = true; \DataMachine\Abilities\Media\ImageTemplateAbilities::ensure_registered(); $assert( 'state 1: when doing_action fires, abilities register immediately via wp_register_ability()', - isset( $GLOBALS['dm_2290_state']->registered['datamachine/render-image-template'] ) - && isset( $GLOBALS['dm_2290_state']->registered['datamachine/list-image-templates'] ) - && empty( $GLOBALS['dm_2290_state']->hooked ) + isset( $GLOBALS['datamachine_2290_state']->registered['datamachine/render-image-template'] ) + && isset( $GLOBALS['datamachine_2290_state']->registered['datamachine/list-image-templates'] ) + && empty( $GLOBALS['datamachine_2290_state']->hooked ) ); // --- State 2: pre-action — hook for later. @@ -222,46 +222,46 @@ public static function can_manage(): bool { return true; } \DataMachine\Abilities\Media\ImageTemplateAbilities::ensure_registered(); $assert( 'state 2: when action has not fired, callback is hooked on wp_abilities_api_init', - isset( $GLOBALS['dm_2290_state']->hooked['wp_abilities_api_init'] ) - && count( $GLOBALS['dm_2290_state']->hooked['wp_abilities_api_init'] ) === 1 - && empty( $GLOBALS['dm_2290_state']->registered ) + isset( $GLOBALS['datamachine_2290_state']->hooked['wp_abilities_api_init'] ) + && count( $GLOBALS['datamachine_2290_state']->hooked['wp_abilities_api_init'] ) === 1 + && empty( $GLOBALS['datamachine_2290_state']->registered ) ); // --- State 3: post-action — no-op instead of mutating registry internals. $reset(); -$GLOBALS['dm_2290_state']->did = 1; +$GLOBALS['datamachine_2290_state']->did = 1; \DataMachine\Abilities\Media\ImageTemplateAbilities::ensure_registered(); $assert( 'state 3: when action already fired, abilities do not register late', - empty( $GLOBALS['dm_2290_state']->registered ) + empty( $GLOBALS['datamachine_2290_state']->registered ) ); $assert( 'state 3: no-op path does not call wp_register_ability() (which would fire _doing_it_wrong)', - 0 === $GLOBALS['dm_2290_state']->doing_it_wrong + 0 === $GLOBALS['datamachine_2290_state']->doing_it_wrong ); $assert( 'state 3: no-op path does not double-hook the action', - empty( $GLOBALS['dm_2290_state']->hooked ) + empty( $GLOBALS['datamachine_2290_state']->hooked ) ); // --- Idempotency: a second post-action ensure_registered() remains a no-op. -$prior_registered = $GLOBALS['dm_2290_state']->registered; +$prior_registered = $GLOBALS['datamachine_2290_state']->registered; \DataMachine\Abilities\Media\ImageTemplateAbilities::ensure_registered(); $assert( 'idempotent: second post-action ensure_registered() call is a no-op', - $GLOBALS['dm_2290_state']->registered === $prior_registered - && 0 === $GLOBALS['dm_2290_state']->doing_it_wrong + $GLOBALS['datamachine_2290_state']->registered === $prior_registered + && 0 === $GLOBALS['datamachine_2290_state']->doing_it_wrong ); // --- Constructor path still works (for the in-runtime defensive call site). $reset(); -$GLOBALS['dm_2290_state']->doing = true; +$GLOBALS['datamachine_2290_state']->doing = true; new \DataMachine\Abilities\Media\ImageTemplateAbilities(); $assert( 'constructor path: instantiating the class triggers ensure_registered()', - isset( $GLOBALS['dm_2290_state']->registered['datamachine/render-image-template'] ) + isset( $GLOBALS['datamachine_2290_state']->registered['datamachine/render-image-template'] ) ); if ( $failed > 0 ) { diff --git a/tests/agents-md-gated-composition-smoke.php b/tests/agents-md-gated-composition-smoke.php index 31b94fc56..0a4dc73a1 100644 --- a/tests/agents-md-gated-composition-smoke.php +++ b/tests/agents-md-gated-composition-smoke.php @@ -46,7 +46,7 @@ function is_multisite(): bool { $failures = array(); -function dm_assert( bool $cond, string $message, array &$failures ): void { +function datamachine_assert( bool $cond, string $message, array &$failures ): void { if ( $cond ) { fwrite( fopen( 'php://stdout', 'w' ), "PASS: {$message}\n" ); return; @@ -62,7 +62,7 @@ function dm_assert( bool $cond, string $message, array &$failures ): void { // --- 1. Gate is constant-only and default-OFF. ----------------------------- -dm_assert( +datamachine_assert( false === datamachine_agents_md_enabled(), 'Gate is OFF when DATAMACHINE_COMPOSE_AGENTS_MD is undefined', $failures @@ -75,13 +75,13 @@ function dm_assert( bool $cond, string $message, array &$failures ): void { // it while OFF, this would fatal. Reaching the next line proves the no-op. datamachine_register_agents_md_file(); datamachine_register_agents_md_sections(); -dm_assert( true, 'File + section registration helpers are no-ops while gate OFF', $failures ); +datamachine_assert( true, 'File + section registration helpers are no-ops while gate OFF', $failures ); // --- 3. CommandRegistry map: analytics is gone, real commands present. ------ $map = \DataMachine\Cli\CommandRegistry::map(); -dm_assert( +datamachine_assert( ! array_key_exists( 'datamachine analytics', $map ), 'CommandRegistry map does NOT contain a `datamachine analytics` entry (relocated to DMB)', $failures @@ -93,9 +93,9 @@ function dm_assert( bool $cond, string $message, array &$failures ): void { $has_analytics_class = true; } } -dm_assert( ! $has_analytics_class, 'No analytics command/class anywhere in the map', $failures ); +datamachine_assert( ! $has_analytics_class, 'No analytics command/class anywhere in the map', $failures ); -dm_assert( +datamachine_assert( isset( $map['datamachine memory'] ) && isset( $map['datamachine drain'] ) && isset( $map['datamachine retention'] ), 'Map contains real core commands (memory, drain, retention)', $failures @@ -122,7 +122,7 @@ public function write(): void {} $names = datamachine_agents_md_reflect_subcommand_names( DM_Smoke_AnnotatedCommand::class ); sort( $names ); -dm_assert( +datamachine_assert( array( 'read', 'write' ) === $names, 'Reflection fallback resolves @subcommand-annotated methods (read, write)', $failures @@ -137,7 +137,7 @@ public function __invoke(): void {} } $invoke_names = datamachine_agents_md_reflect_subcommand_names( DM_Smoke_InvokeCommand::class ); -dm_assert( +datamachine_assert( array() === $invoke_names, 'Reflection fallback omits a flat __invoke command from the subcommand list', $failures diff --git a/tests/batch-child-agent-id-smoke.php b/tests/batch-child-agent-id-smoke.php index 72c7340ee..565cdc4a3 100644 --- a/tests/batch-child-agent-id-smoke.php +++ b/tests/batch-child-agent-id-smoke.php @@ -87,7 +87,7 @@ function strip_flow_runtime_queue_payloads( array $engine_snapshot ): array { return $engine_snapshot; } -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -115,14 +115,14 @@ function dm_assert( bool $cond, string $msg ): void { $child = build_child_engine_data( $parent_snapshot, 65, 64, '2', '2' ); -dm_assert( 65 === $child['job']['job_id'], 'child job_id replaces parent' ); -dm_assert( 64 === $child['job']['parent_job_id'], 'parent_job_id set' ); -dm_assert( 2 === $child['job']['agent_id'], 'agent_id carried from parent' ); -dm_assert( 1 === $child['job']['user_id'], 'user_id carried from parent' ); -dm_assert( '2' === $child['job']['pipeline_id'], 'pipeline_id preserved' ); -dm_assert( '2' === $child['job']['flow_id'], 'flow_id preserved' ); -dm_assert( isset( $child['flow_config'] ), 'engine_snapshot keys preserved (flow_config)' ); -dm_assert( isset( $child['pipeline_config'] ), 'engine_snapshot keys preserved (pipeline_config)' ); +datamachine_assert( 65 === $child['job']['job_id'], 'child job_id replaces parent' ); +datamachine_assert( 64 === $child['job']['parent_job_id'], 'parent_job_id set' ); +datamachine_assert( 2 === $child['job']['agent_id'], 'agent_id carried from parent' ); +datamachine_assert( 1 === $child['job']['user_id'], 'user_id carried from parent' ); +datamachine_assert( '2' === $child['job']['pipeline_id'], 'pipeline_id preserved' ); +datamachine_assert( '2' === $child['job']['flow_id'], 'flow_id preserved' ); +datamachine_assert( isset( $child['flow_config'] ), 'engine_snapshot keys preserved (flow_config)' ); +datamachine_assert( isset( $child['pipeline_config'] ), 'engine_snapshot keys preserved (pipeline_config)' ); // ----------------------------------------------------------------- echo "\n[1b] runtime queue payloads are not cloned into child engine_data\n"; @@ -156,13 +156,13 @@ function dm_assert( bool $cond, string $msg ): void { $child = build_child_engine_data( $parent_snapshot, 65, 64, '2', '2' ); -dm_assert( isset( $child['flow_config']['fetch_step']['queue_mode'] ), 'queue_mode preserved for queue consumers' ); -dm_assert( 'drain' === $child['flow_config']['fetch_step']['queue_mode'], 'fetch queue_mode value preserved' ); -dm_assert( ! isset( $child['flow_config']['fetch_step']['config_patch_queue'] ), 'config_patch_queue stripped from child engine_data' ); -dm_assert( ! isset( $child['flow_config']['fetch_step']['prompt_queue'] ), 'prompt_queue stripped from child engine_data' ); -dm_assert( ! isset( $child['flow_config']['fetch_step']['_queue_consume_revision'] ), 'queue revision stripped from child engine_data' ); -dm_assert( ! isset( $child['flow_config']['ai_step']['prompt_queue'] ), 'prompt_queue stripped from later steps too' ); -dm_assert( 'ai' === $child['flow_config']['ai_step']['step_type'], 'non-runtime step definition fields preserved' ); +datamachine_assert( isset( $child['flow_config']['fetch_step']['queue_mode'] ), 'queue_mode preserved for queue consumers' ); +datamachine_assert( 'drain' === $child['flow_config']['fetch_step']['queue_mode'], 'fetch queue_mode value preserved' ); +datamachine_assert( ! isset( $child['flow_config']['fetch_step']['config_patch_queue'] ), 'config_patch_queue stripped from child engine_data' ); +datamachine_assert( ! isset( $child['flow_config']['fetch_step']['prompt_queue'] ), 'prompt_queue stripped from child engine_data' ); +datamachine_assert( ! isset( $child['flow_config']['fetch_step']['_queue_consume_revision'] ), 'queue revision stripped from child engine_data' ); +datamachine_assert( ! isset( $child['flow_config']['ai_step']['prompt_queue'] ), 'prompt_queue stripped from later steps too' ); +datamachine_assert( 'ai' === $child['flow_config']['ai_step']['step_type'], 'non-runtime step definition fields preserved' ); // ----------------------------------------------------------------- echo "\n[2] parent without agent_id — child gets null (not 0, not garbage)\n"; @@ -178,8 +178,8 @@ function dm_assert( bool $cond, string $msg ): void { $child = build_child_engine_data( $parent_snapshot, 101, 100, '3', '5' ); -dm_assert( null === $child['job']['agent_id'], 'agent_id is null when absent on parent' ); -dm_assert( 1 === $child['job']['user_id'], 'user_id still carried' ); +datamachine_assert( null === $child['job']['agent_id'], 'agent_id is null when absent on parent' ); +datamachine_assert( 1 === $child['job']['user_id'], 'user_id still carried' ); // ----------------------------------------------------------------- echo "\n[3] parent with agent_id=0 — treated as absent (null on child)\n"; @@ -193,7 +193,7 @@ function dm_assert( bool $cond, string $msg ): void { $child = build_child_engine_data( $parent_snapshot, 201, 200, '1', '1' ); -dm_assert( null === $child['job']['agent_id'], 'agent_id=0 on parent → null on child' ); +datamachine_assert( null === $child['job']['agent_id'], 'agent_id=0 on parent → null on child' ); // ----------------------------------------------------------------- echo "\n[4] parent with no user_id — null carried forward\n"; @@ -206,8 +206,8 @@ function dm_assert( bool $cond, string $msg ): void { $child = build_child_engine_data( $parent_snapshot, 301, 300, '1', '1' ); -dm_assert( 2 === $child['job']['agent_id'], 'agent_id carried' ); -dm_assert( null === $child['job']['user_id'], 'user_id null when absent' ); +datamachine_assert( 2 === $child['job']['agent_id'], 'agent_id carried' ); +datamachine_assert( null === $child['job']['user_id'], 'user_id null when absent' ); // ----------------------------------------------------------------- echo "\n[5] empty parent job — child has minimal context\n"; @@ -217,10 +217,10 @@ function dm_assert( bool $cond, string $msg ): void { $child = build_child_engine_data( $parent_snapshot, 401, 400, '1', '1' ); -dm_assert( null === $child['job']['agent_id'], 'agent_id null' ); -dm_assert( null === $child['job']['user_id'], 'user_id null' ); -dm_assert( 401 === $child['job']['job_id'], 'job_id still set' ); -dm_assert( 400 === $child['job']['parent_job_id'], 'parent_job_id still set' ); +datamachine_assert( null === $child['job']['agent_id'], 'agent_id null' ); +datamachine_assert( null === $child['job']['user_id'], 'user_id null' ); +datamachine_assert( 401 === $child['job']['job_id'], 'job_id still set' ); +datamachine_assert( 400 === $child['job']['parent_job_id'], 'parent_job_id still set' ); // ----------------------------------------------------------------- echo "\n[6] real-world WooCommerce flow shape (job 64 → child 65 from session)\n"; @@ -246,9 +246,9 @@ function dm_assert( bool $cond, string $msg ): void { // causing CoreMemoryFilesDirective to fall back to the user_id default-agent // lookup which returns the WRONG agent (the install's primary agent, not // the wiki-generator the pipeline is bound to). -dm_assert( 2 === $child['job']['agent_id'], 'wiki-generator agent_id (2) carried to child' ); -dm_assert( 64 === $child['job']['parent_job_id'], 'parent linkage preserved' ); -dm_assert( 'wiki-generator: WooCommerce releases' === $child['pipeline']['name'], 'pipeline metadata preserved on child' ); -dm_assert( 'WC backfill — mgs (queueable test)' === $child['flow']['name'], 'flow metadata preserved on child' ); +datamachine_assert( 2 === $child['job']['agent_id'], 'wiki-generator agent_id (2) carried to child' ); +datamachine_assert( 64 === $child['job']['parent_job_id'], 'parent linkage preserved' ); +datamachine_assert( 'wiki-generator: WooCommerce releases' === $child['pipeline']['name'], 'pipeline metadata preserved on child' ); +datamachine_assert( 'WC backfill — mgs (queueable test)' === $child['flow']['name'], 'flow metadata preserved on child' ); echo "\n=== batch-child-agent-id-smoke: ALL PASS ===\n"; diff --git a/tests/batch-completion-strategy-smoke.php b/tests/batch-completion-strategy-smoke.php index d95e38568..11cb39ebc 100644 --- a/tests/batch-completion-strategy-smoke.php +++ b/tests/batch-completion-strategy-smoke.php @@ -15,7 +15,7 @@ define( 'ABSPATH', __DIR__ . '/' ); } -function dm_strategy_assert( bool $cond, string $msg ): void { +function datamachine_strategy_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -24,7 +24,7 @@ function dm_strategy_assert( bool $cond, string $msg ): void { exit( 1 ); } -function dm_strategy_read( string $relative_path ): string { +function datamachine_strategy_read( string $relative_path ): string { $path = dirname( __DIR__ ) . '/' . $relative_path; $body = file_get_contents( $path ); if ( false === $body ) { @@ -57,80 +57,80 @@ function build_task_batch_status_strategy( array $engine_data ): string { echo "=== batch-completion-strategy-smoke ===\n"; -$batch_scheduler = dm_strategy_read( 'inc/Core/ActionScheduler/BatchScheduler.php' ); -$pipeline_batch = dm_strategy_read( 'inc/Abilities/Engine/PipelineBatchScheduler.php' ); -$task_scheduler = dm_strategy_read( 'inc/Engine/Tasks/TaskScheduler.php' ); +$batch_scheduler = datamachine_strategy_read( 'inc/Core/ActionScheduler/BatchScheduler.php' ); +$pipeline_batch = datamachine_strategy_read( 'inc/Abilities/Engine/PipelineBatchScheduler.php' ); +$task_scheduler = datamachine_strategy_read( 'inc/Engine/Tasks/TaskScheduler.php' ); $children_strategy = 'children_complete'; $chunks_strategy = 'chunks_scheduled'; // ----------------------------------------------------------------- echo "\n[1] BatchScheduler declares and stores completion strategy metadata\n"; -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $batch_scheduler, "COMPLETION_STRATEGY_CHILDREN_COMPLETE = '{$children_strategy}'" ), 'children_complete strategy constant exists' ); -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $batch_scheduler, "COMPLETION_STRATEGY_CHUNKS_SCHEDULED = '{$chunks_strategy}'" ), 'chunks_scheduled strategy constant exists' ); -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $batch_scheduler, "'batch_completion_strategy' => \$completion_strategy" ), 'BatchScheduler::start stores batch_completion_strategy in engine_data' ); $metadata = build_batch_metadata( 25, 10, 'pipeline', $children_strategy ); -dm_strategy_assert( true === $metadata['batch'], 'metadata still marks the parent as a batch' ); -dm_strategy_assert( 25 === $metadata['batch_total'], 'metadata still stores batch_total' ); -dm_strategy_assert( 0 === $metadata['batch_scheduled'], 'metadata still initializes batch_scheduled to 0' ); -dm_strategy_assert( 10 === $metadata['batch_chunk_size'], 'metadata still stores batch_chunk_size' ); -dm_strategy_assert( 'pipeline' === $metadata['batch_context'], 'metadata still stores batch_context' ); -dm_strategy_assert( $children_strategy === $metadata['batch_completion_strategy'], 'metadata stores declared completion strategy' ); +datamachine_strategy_assert( true === $metadata['batch'], 'metadata still marks the parent as a batch' ); +datamachine_strategy_assert( 25 === $metadata['batch_total'], 'metadata still stores batch_total' ); +datamachine_strategy_assert( 0 === $metadata['batch_scheduled'], 'metadata still initializes batch_scheduled to 0' ); +datamachine_strategy_assert( 10 === $metadata['batch_chunk_size'], 'metadata still stores batch_chunk_size' ); +datamachine_strategy_assert( 'pipeline' === $metadata['batch_context'], 'metadata still stores batch_context' ); +datamachine_strategy_assert( $children_strategy === $metadata['batch_completion_strategy'], 'metadata stores declared completion strategy' ); // ----------------------------------------------------------------- echo "\n[2] Pipeline fan-out declares children_complete\n"; -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $pipeline_batch, 'BatchScheduler::COMPLETION_STRATEGY_CHILDREN_COMPLETE' ), 'PipelineBatchScheduler::fanOut passes children_complete to BatchScheduler::start' ); -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $pipeline_batch, 'public static function onChildComplete' ), 'pipeline completion still lives in onChildComplete()' ); -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $pipeline_batch, '$active > 0 || $total_children < $batch_total' ), 'pipeline parent still waits for children to finish and all children to be scheduled' ); -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $pipeline_batch, '$jobs_db->complete_job( (int) $parent_job_id, $parent_status );' ), 'pipeline parent status is still completed from child aggregation' ); // ----------------------------------------------------------------- echo "\n[3] System-task fan-out declares chunks_scheduled\n"; -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $task_scheduler, 'BatchScheduler::COMPLETION_STRATEGY_CHUNKS_SCHEDULED' ), 'TaskScheduler::scheduleBatch passes chunks_scheduled to BatchScheduler::start' ); -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $task_scheduler, "if ( ! \$result['more'] )" ), 'task batch still completes on the final chunk path' ); -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $task_scheduler, '$jobs_db->complete_job( $parent_job_id, JobStatus::COMPLETED );' ), 'task batch parent still completes after chunks are scheduled' ); $metadata = build_batch_metadata( 25, 10, 'task', $chunks_strategy ); -dm_strategy_assert( 'task' === $metadata['batch_context'], 'task metadata still stores batch_context=task' ); -dm_strategy_assert( $chunks_strategy === $metadata['batch_completion_strategy'], 'task metadata stores chunks_scheduled strategy' ); +datamachine_strategy_assert( 'task' === $metadata['batch_context'], 'task metadata still stores batch_context=task' ); +datamachine_strategy_assert( $chunks_strategy === $metadata['batch_completion_strategy'], 'task metadata stores chunks_scheduled strategy' ); // ----------------------------------------------------------------- echo "\n[4] Task batch status exposes strategy without changing behavior\n"; -dm_strategy_assert( +datamachine_strategy_assert( str_contains( $task_scheduler, "'completion_strategy' => \$engine_data['batch_completion_strategy'] ?? ''" ), 'getBatchStatus exposes completion_strategy from engine_data' ); -dm_strategy_assert( $chunks_strategy === build_task_batch_status_strategy( $metadata ), 'status helper resolves stored strategy' ); -dm_strategy_assert( '' === build_task_batch_status_strategy( array() ), 'missing legacy strategy resolves to empty string' ); +datamachine_strategy_assert( $chunks_strategy === build_task_batch_status_strategy( $metadata ), 'status helper resolves stored strategy' ); +datamachine_strategy_assert( '' === build_task_batch_status_strategy( array() ), 'missing legacy strategy resolves to empty string' ); echo "\n=== batch-completion-strategy-smoke: ALL PASS ===\n"; diff --git a/tests/daily-memory-overflow-split-smoke.php b/tests/daily-memory-overflow-split-smoke.php index 099269a58..15a72bd84 100644 --- a/tests/daily-memory-overflow-split-smoke.php +++ b/tests/daily-memory-overflow-split-smoke.php @@ -12,7 +12,7 @@ $failures = array(); $passes = 0; -function dm_overflow_assert( bool $condition, string $label, array &$failures, int &$passes ): void { +function datamachine_overflow_assert( bool $condition, string $label, array &$failures, int &$passes ): void { if ( $condition ) { ++$passes; echo "PASS: {$label}\n"; @@ -36,13 +36,13 @@ function add_action( string $_hook, callable $_callback, int $_priority = 10, in require_once __DIR__ . '/../inc/Engine/AI/System/Tasks/DailyMemoryTask.php'; $source = (string) file_get_contents( __DIR__ . '/../inc/Engine/AI/System/Tasks/DailyMemoryTask.php' ); -dm_overflow_assert( str_contains( $source, 'maybeHandleDeterministicOverflow' ), 'production task has deterministic overflow hook', $failures, $passes ); -dm_overflow_assert( ! str_contains( $source, 'splitMemorySectionsForOverflow' ), 'local section-split helper is removed', $failures, $passes ); -dm_overflow_assert( str_contains( $source, 'WP_Agent_Markdown_Section_Compaction_Adapter::split_for_overflow' ), 'overflow decision is delegated to Agents API markdown compaction', $failures, $passes ); -dm_overflow_assert( str_contains( $source, 'WP_Agent_Markdown_Section_Compaction_Adapter::parse' ), 'markdown projection uses Agents API adapter', $failures, $passes ); -dm_overflow_assert( ! str_contains( $source, 'WP_Agent_Message::text' ), 'fake conversation-message projection is removed', $failures, $passes ); -dm_overflow_assert( str_contains( $source, 'datamachine_daily_memory_overflow_threshold' ), 'overflow threshold is filterable', $failures, $passes ); -dm_overflow_assert( str_contains( $source, 'datamachine_daily_memory_overflow_target_size' ), 'overflow target size is filterable', $failures, $passes ); +datamachine_overflow_assert( str_contains( $source, 'maybeHandleDeterministicOverflow' ), 'production task has deterministic overflow hook', $failures, $passes ); +datamachine_overflow_assert( ! str_contains( $source, 'splitMemorySectionsForOverflow' ), 'local section-split helper is removed', $failures, $passes ); +datamachine_overflow_assert( str_contains( $source, 'WP_Agent_Markdown_Section_Compaction_Adapter::split_for_overflow' ), 'overflow decision is delegated to Agents API markdown compaction', $failures, $passes ); +datamachine_overflow_assert( str_contains( $source, 'WP_Agent_Markdown_Section_Compaction_Adapter::parse' ), 'markdown projection uses Agents API adapter', $failures, $passes ); +datamachine_overflow_assert( ! str_contains( $source, 'WP_Agent_Message::text' ), 'fake conversation-message projection is removed', $failures, $passes ); +datamachine_overflow_assert( str_contains( $source, 'datamachine_daily_memory_overflow_threshold' ), 'overflow threshold is filterable', $failures, $passes ); +datamachine_overflow_assert( str_contains( $source, 'datamachine_daily_memory_overflow_target_size' ), 'overflow target size is filterable', $failures, $passes ); $method = new ReflectionMethod( DataMachine\Engine\AI\System\Tasks\DailyMemoryTask::class, 'planMemoryOverflowArchive' ); @@ -55,19 +55,19 @@ function add_action( string $_hook, callable $_callback, int $_priority = 10, in } $split = $method->invoke( null, $content, 8192, '2026-05-01' ); -dm_overflow_assert( '' !== $split['archived'], 'oversized input produces archive content', $failures, $passes ); -dm_overflow_assert( str_contains( $split['persistent'], 'Archived Memory Overflow' ), 'persistent output includes archive pointer', $failures, $passes ); -dm_overflow_assert( str_contains( $split['persistent'], 'daily/2026/05/01.md' ), 'archive pointer names daily file path', $failures, $passes ); -dm_overflow_assert( str_contains( $split['persistent'], '## Section 1' ), 'persistent output keeps early sections', $failures, $passes ); -dm_overflow_assert( strlen( $content ) > 250000, '~250KB live stress input shape is covered', $failures, $passes ); -dm_overflow_assert( strlen( $split['persistent'] ) <= 8192, 'persistent output reduces to target size', $failures, $passes ); -dm_overflow_assert( str_contains( $split['archived'], $sections[90] ), 'archive output keeps later sections verbatim', $failures, $passes ); -dm_overflow_assert( ! str_contains( $split['persistent'], '## Section 90' ), 'archived tail sections are removed from persistent output', $failures, $passes ); -dm_overflow_assert( $split['persistent_blocks'] > 0, 'persistent block count reported', $failures, $passes ); -dm_overflow_assert( $split['archived_blocks'] > 0, 'archived block count reported', $failures, $passes ); +datamachine_overflow_assert( '' !== $split['archived'], 'oversized input produces archive content', $failures, $passes ); +datamachine_overflow_assert( str_contains( $split['persistent'], 'Archived Memory Overflow' ), 'persistent output includes archive pointer', $failures, $passes ); +datamachine_overflow_assert( str_contains( $split['persistent'], 'daily/2026/05/01.md' ), 'archive pointer names daily file path', $failures, $passes ); +datamachine_overflow_assert( str_contains( $split['persistent'], '## Section 1' ), 'persistent output keeps early sections', $failures, $passes ); +datamachine_overflow_assert( strlen( $content ) > 250000, '~250KB live stress input shape is covered', $failures, $passes ); +datamachine_overflow_assert( strlen( $split['persistent'] ) <= 8192, 'persistent output reduces to target size', $failures, $passes ); +datamachine_overflow_assert( str_contains( $split['archived'], $sections[90] ), 'archive output keeps later sections verbatim', $failures, $passes ); +datamachine_overflow_assert( ! str_contains( $split['persistent'], '## Section 90' ), 'archived tail sections are removed from persistent output', $failures, $passes ); +datamachine_overflow_assert( $split['persistent_blocks'] > 0, 'persistent block count reported', $failures, $passes ); +datamachine_overflow_assert( $split['archived_blocks'] > 0, 'archived block count reported', $failures, $passes ); $small = $method->invoke( null, "## Only\n\nSmall file.\n", 1400, '2026-05-01' ); -dm_overflow_assert( '' === $small['archived'], 'single-section small input does not split', $failures, $passes ); +datamachine_overflow_assert( '' === $small['archived'], 'single-section small input does not split', $failures, $passes ); echo "\n{$passes} passed, " . count( $failures ) . " failed\n"; if ( ! empty( $failures ) ) { diff --git a/tests/exclude-keywords-smoke.php b/tests/exclude-keywords-smoke.php index 083325071..7d9131dbd 100644 --- a/tests/exclude-keywords-smoke.php +++ b/tests/exclude-keywords-smoke.php @@ -36,7 +36,7 @@ function __( $text, $domain = '' ) { use DataMachine\Abilities\Fetch\FetchRssAbility; -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -67,33 +67,33 @@ function call_search( FetchRssAbility $ability, string $text, string $search ): $ability = new FetchRssAbility(); echo "applyKeywordExclusion — empty exclusion list never matches\n"; -dm_assert( false === call_exclude( $ability, 'Anything', '' ), 'empty string returns false' ); -dm_assert( false === call_exclude( $ability, 'Anything', ' ' ), 'whitespace-only returns false' ); -dm_assert( false === call_exclude( $ability, 'Anything', ',,,' ), 'commas-only returns false' ); +datamachine_assert( false === call_exclude( $ability, 'Anything', '' ), 'empty string returns false' ); +datamachine_assert( false === call_exclude( $ability, 'Anything', ' ' ), 'whitespace-only returns false' ); +datamachine_assert( false === call_exclude( $ability, 'Anything', ',,,' ), 'commas-only returns false' ); echo "applyKeywordExclusion — single keyword match\n"; -dm_assert( true === call_exclude( $ability, 'Foo dot release bar', 'dot release' ), 'substring matches' ); -dm_assert( true === call_exclude( $ability, 'FOO DOT RELEASE BAR', 'dot release' ), 'case-insensitive uppercase text' ); -dm_assert( true === call_exclude( $ability, 'Foo Dot Release Bar', 'DOT RELEASE' ), 'case-insensitive uppercase keyword' ); +datamachine_assert( true === call_exclude( $ability, 'Foo dot release bar', 'dot release' ), 'substring matches' ); +datamachine_assert( true === call_exclude( $ability, 'FOO DOT RELEASE BAR', 'dot release' ), 'case-insensitive uppercase text' ); +datamachine_assert( true === call_exclude( $ability, 'Foo Dot Release Bar', 'DOT RELEASE' ), 'case-insensitive uppercase keyword' ); echo "applyKeywordExclusion — non-matching\n"; -dm_assert( false === call_exclude( $ability, 'Foo bar baz', 'qux' ), 'unrelated keyword returns false' ); -dm_assert( false === call_exclude( $ability, '', 'qux' ), 'empty text returns false' ); +datamachine_assert( false === call_exclude( $ability, 'Foo bar baz', 'qux' ), 'unrelated keyword returns false' ); +datamachine_assert( false === call_exclude( $ability, '', 'qux' ), 'empty text returns false' ); echo "applyKeywordExclusion — multiple comma-separated keywords (any-match)\n"; -dm_assert( true === call_exclude( $ability, 'A B C D', 'X, B, Y' ), 'second keyword matches' ); -dm_assert( true === call_exclude( $ability, 'A B C D', 'A,X,Y' ), 'first keyword matches' ); -dm_assert( false === call_exclude( $ability, 'A B C D', 'X, Y, Z' ), 'no keyword matches' ); -dm_assert( true === call_exclude( $ability, 'A B C D', ' ,B, ' ), 'empty terms ignored, real term matches' ); +datamachine_assert( true === call_exclude( $ability, 'A B C D', 'X, B, Y' ), 'second keyword matches' ); +datamachine_assert( true === call_exclude( $ability, 'A B C D', 'A,X,Y' ), 'first keyword matches' ); +datamachine_assert( false === call_exclude( $ability, 'A B C D', 'X, Y, Z' ), 'no keyword matches' ); +datamachine_assert( true === call_exclude( $ability, 'A B C D', ' ,B, ' ), 'empty terms ignored, real term matches' ); echo "applyKeywordExclusion vs applyKeywordSearch — inverse semantics\n"; // "search" is include-first: empty search means "keep everything"; non-empty must match to keep. // "exclude" is the inverse: empty means "skip nothing"; non-empty must match to skip. -dm_assert( true === call_search( $ability, 'A B', '' ), 'empty search keeps item' ); -dm_assert( false === call_exclude( $ability, 'A B', '' ), 'empty exclude does NOT skip item' ); -dm_assert( true === call_search( $ability, 'A B', 'B' ), 'matching search keeps item' ); -dm_assert( true === call_exclude( $ability, 'A B', 'B' ), 'matching exclude SKIPS item' ); -dm_assert( false === call_search( $ability, 'A B', 'C' ), 'non-matching search drops item' ); -dm_assert( false === call_exclude( $ability, 'A B', 'C' ), 'non-matching exclude does NOT skip item' ); +datamachine_assert( true === call_search( $ability, 'A B', '' ), 'empty search keeps item' ); +datamachine_assert( false === call_exclude( $ability, 'A B', '' ), 'empty exclude does NOT skip item' ); +datamachine_assert( true === call_search( $ability, 'A B', 'B' ), 'matching search keeps item' ); +datamachine_assert( true === call_exclude( $ability, 'A B', 'B' ), 'matching exclude SKIPS item' ); +datamachine_assert( false === call_search( $ability, 'A B', 'C' ), 'non-matching search drops item' ); +datamachine_assert( false === call_exclude( $ability, 'A B', 'C' ), 'non-matching exclude does NOT skip item' ); echo "\n[OK] All exclude_keywords smoke tests passed.\n"; diff --git a/tests/execute-workflow-initial-data-contract-smoke.php b/tests/execute-workflow-initial-data-contract-smoke.php index ecc473669..7fbabe03a 100644 --- a/tests/execute-workflow-initial-data-contract-smoke.php +++ b/tests/execute-workflow-initial-data-contract-smoke.php @@ -61,7 +61,7 @@ function build_execute_workflow_engine_data_for_contract_test( int $job_id, arra return $engine_data; } -function dm_assert_same( $expected, $actual, string $msg ): void { +function datamachine_assert_same( $expected, $actual, string $msg ): void { if ( $expected === $actual ) { echo " [PASS] {$msg}\n"; return; @@ -110,24 +110,24 @@ function dm_assert_same( $expected, $actual, string $msg ): void { ); $engine_data = build_execute_workflow_engine_data_for_contract_test( 100, $configs, $initial_data ); -dm_assert_same( $generated_flow_config, $engine_data['flow_config'], 'generated flow_config is preserved' ); -dm_assert_same( false, isset( $engine_data['flow_config']['poisoned_flow'] ), 'caller flow_config is not retained' ); -dm_assert_same( $generated_pipeline_config, $engine_data['pipeline_config'], 'generated pipeline_config is preserved' ); -dm_assert_same( false, isset( $engine_data['pipeline_config']['poisoned_pipeline'] ), 'caller pipeline_config is not retained' ); -dm_assert_same( 'wiki_maintain_article', $engine_data['task_type'], 'non-reserved initial_data remains available' ); +datamachine_assert_same( $generated_flow_config, $engine_data['flow_config'], 'generated flow_config is preserved' ); +datamachine_assert_same( false, isset( $engine_data['flow_config']['poisoned_flow'] ), 'caller flow_config is not retained' ); +datamachine_assert_same( $generated_pipeline_config, $engine_data['pipeline_config'], 'generated pipeline_config is preserved' ); +datamachine_assert_same( false, isset( $engine_data['pipeline_config']['poisoned_pipeline'] ), 'caller pipeline_config is not retained' ); +datamachine_assert_same( 'wiki_maintain_article', $engine_data['task_type'], 'non-reserved initial_data remains available' ); echo "\n[2] parent_job_id still routes to create_job args\n"; $create_args = build_execute_workflow_create_args_for_contract_test( $initial_data ); -dm_assert_same( 64, $create_args['parent_job_id'] ?? null, 'parent_job_id preserved for indexed job linkage' ); +datamachine_assert_same( 64, $create_args['parent_job_id'] ?? null, 'parent_job_id preserved for indexed job linkage' ); echo "\n[3] flat identity fields still route into the job snapshot\n"; -dm_assert_same( 2, $engine_data['agent_id'], 'flat agent_id remains in engine_data' ); -dm_assert_same( 'Wayward Son', $engine_data['agent_slug'], 'flat agent_slug remains in engine_data' ); -dm_assert_same( 1, $engine_data['user_id'], 'flat user_id remains in engine_data' ); -dm_assert_same( 100, $engine_data['job']['job_id'], 'authoritative job_id wins in job snapshot' ); -dm_assert_same( 2, $engine_data['job']['agent_id'], 'flat agent_id fills missing job.agent_id' ); -dm_assert_same( 'wayward-son', $engine_data['job']['agent_slug'], 'flat agent_slug fills missing job.agent_slug' ); -dm_assert_same( 1, $engine_data['job']['user_id'], 'job.user_id preserved from caller snapshot' ); +datamachine_assert_same( 2, $engine_data['agent_id'], 'flat agent_id remains in engine_data' ); +datamachine_assert_same( 'Wayward Son', $engine_data['agent_slug'], 'flat agent_slug remains in engine_data' ); +datamachine_assert_same( 1, $engine_data['user_id'], 'flat user_id remains in engine_data' ); +datamachine_assert_same( 100, $engine_data['job']['job_id'], 'authoritative job_id wins in job snapshot' ); +datamachine_assert_same( 2, $engine_data['job']['agent_id'], 'flat agent_id fills missing job.agent_id' ); +datamachine_assert_same( 'wayward-son', $engine_data['job']['agent_slug'], 'flat agent_slug fills missing job.agent_slug' ); +datamachine_assert_same( 1, $engine_data['job']['user_id'], 'job.user_id preserved from caller snapshot' ); echo "\n[4] caller job snapshot identity is preserved when provided\n"; $initial_data_with_snapshot = array( @@ -140,16 +140,16 @@ function dm_assert_same( $expected, $actual, string $msg ): void { ), ); $engine_data_with_snapshot = build_execute_workflow_engine_data_for_contract_test( 101, $configs, $initial_data_with_snapshot ); -dm_assert_same( 9, $engine_data_with_snapshot['job']['agent_id'], 'caller job.agent_id remains authoritative over flat agent_id' ); -dm_assert_same( 8, $engine_data_with_snapshot['job']['user_id'], 'caller job.user_id remains authoritative over flat user_id' ); -dm_assert_same( 101, $engine_data_with_snapshot['job']['job_id'], 'engine job_id remains authoritative over caller job.job_id' ); +datamachine_assert_same( 9, $engine_data_with_snapshot['job']['agent_id'], 'caller job.agent_id remains authoritative over flat agent_id' ); +datamachine_assert_same( 8, $engine_data_with_snapshot['job']['user_id'], 'caller job.user_id remains authoritative over flat user_id' ); +datamachine_assert_same( 101, $engine_data_with_snapshot['job']['job_id'], 'engine job_id remains authoritative over caller job.job_id' ); echo "\n[5] direct workflow packet handoff is engine-backed\n"; $schedule_next_step_source = file_get_contents( dirname( __DIR__ ) . '/inc/Abilities/Engine/ScheduleNextStepAbility.php' ) ?: ''; $execute_step_source = file_get_contents( dirname( __DIR__ ) . '/inc/Abilities/Engine/ExecuteStepAbility.php' ) ?: ''; -dm_assert_same( true, str_contains( $schedule_next_step_source, "'direct' === \$raw_flow_id" ), 'direct schedule path is detected before file storage' ); -dm_assert_same( true, str_contains( $schedule_next_step_source, 'direct_step_data_packets' ), 'direct schedule path stores data packets on engine data' ); -dm_assert_same( true, str_contains( $execute_step_source, "empty( \$dataPackets ) && 'direct' === \$flow_id" ), 'direct execute path falls back when file storage has no packets' ); -dm_assert_same( true, str_contains( $execute_step_source, 'direct_step_data_packets' ), 'direct execute path reloads engine-backed packets' ); +datamachine_assert_same( true, str_contains( $schedule_next_step_source, "'direct' === \$raw_flow_id" ), 'direct schedule path is detected before file storage' ); +datamachine_assert_same( true, str_contains( $schedule_next_step_source, 'direct_step_data_packets' ), 'direct schedule path stores data packets on engine data' ); +datamachine_assert_same( true, str_contains( $execute_step_source, "empty( \$dataPackets ) && 'direct' === \$flow_id" ), 'direct execute path falls back when file storage has no packets' ); +datamachine_assert_same( true, str_contains( $execute_step_source, 'direct_step_data_packets' ), 'direct execute path reloads engine-backed packets' ); echo "\n=== execute-workflow-initial-data-contract-smoke: ALL PASS ===\n"; diff --git a/tests/flows-list-config-placeholder-smoke.php b/tests/flows-list-config-placeholder-smoke.php index ccab13362..43110bb9c 100644 --- a/tests/flows-list-config-placeholder-smoke.php +++ b/tests/flows-list-config-placeholder-smoke.php @@ -35,7 +35,7 @@ * @param string $label Test label. * @param string $detail Optional detail. */ -function dm_assert( bool $cond, string $label, string $detail = '' ): void { +function datamachine_assert( bool $cond, string $label, string $detail = '' ): void { global $failures; if ( $cond ) { echo " [PASS] {$label}\n"; @@ -56,35 +56,35 @@ function dm_assert( bool $cond, string $label, string $detail = '' ): void { * @param string $format Output format. * @return string Cell value. */ -function dm_list_cell( string $summary, string $format ): string { +function datamachine_list_cell( string $summary, string $format ): string { $is_table = 'table' === $format; return ( $is_table && '' === $summary ) ? '—' : $summary; } echo "\n[1] table + empty summary → em-dash placeholder (human display)\n"; -dm_assert( '—' === dm_list_cell( '', 'table' ), 'empty config in table shows "—"' ); +datamachine_assert( '—' === datamachine_list_cell( '', 'table' ), 'empty config in table shows "—"' ); echo "\n[2] structured formats + empty summary → honest empty string (no dash)\n"; foreach ( array( 'json', 'csv', 'yaml', 'ids', 'count' ) as $fmt ) { - dm_assert( - '' === dm_list_cell( '', $fmt ), + datamachine_assert( + '' === datamachine_list_cell( '', $fmt ), "empty config in {$fmt} is '' not '—'", - "got: '" . dm_list_cell( '', $fmt ) . "'" + "got: '" . datamachine_list_cell( '', $fmt ) . "'" ); } echo "\n[3] real summary passes through verbatim in every format\n"; $real = 'city=Charleston | r=50'; foreach ( array( 'table', 'json', 'csv', 'yaml' ) as $fmt ) { - dm_assert( - $real === dm_list_cell( $real, $fmt ), + datamachine_assert( + $real === datamachine_list_cell( $real, $fmt ), "real config preserved in {$fmt}" ); } echo "\n[4] the literal em-dash never appears in structured output for an empty cell\n"; -$json_cell = dm_list_cell( '', 'json' ); -dm_assert( +$json_cell = datamachine_list_cell( '', 'json' ); +datamachine_assert( false === strpos( $json_cell, '—' ), 'json cell contains no em-dash', "got: '{$json_cell}'" @@ -93,19 +93,19 @@ function dm_list_cell( string $summary, string $format ): string { echo "\n[5] source guard: extractors no longer hardcode the '—' fallback\n"; $flows_src = (string) file_get_contents( __DIR__ . '/../inc/Cli/Commands/Flows/FlowsCommand.php' ); $pipelines_src = (string) file_get_contents( __DIR__ . '/../inc/Cli/Commands/PipelinesCommand.php' ); -dm_assert( +datamachine_assert( false === strpos( $flows_src, "? \$summary : '—'" ), 'FlowsCommand::extractConfigSummary dropped the inline "—" fallback' ); -dm_assert( +datamachine_assert( false === strpos( $pipelines_src, "? \$summary : '—'" ), 'PipelinesCommand::extractPipelineLocation dropped the inline "—" fallback' ); -dm_assert( +datamachine_assert( false !== strpos( $flows_src, "( \$is_table && '' === \$config_summary ) ? '—'" ), 'FlowsCommand applies the "—" placeholder only for table format' ); -dm_assert( +datamachine_assert( false !== strpos( $pipelines_src, "( \$is_table && '' === \$location ) ? '—'" ), 'PipelinesCommand applies the "—" placeholder only for table format' ); diff --git a/tests/jobs-command-undo-fanout-smoke.php b/tests/jobs-command-undo-fanout-smoke.php index 1e729d3fe..0952ff2c9 100644 --- a/tests/jobs-command-undo-fanout-smoke.php +++ b/tests/jobs-command-undo-fanout-smoke.php @@ -202,7 +202,7 @@ function run_undo_loop_body( $jobs_db->store_engine_data( $jid, $engine_data ); } -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -237,13 +237,13 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $parent, $task, $jobs_db, false, false, $reverted, $skipped, $failed ); -dm_assert( $task->undo_called, 'task->undo() called even though parent has no own effects (the bug fix)' ); -dm_assert( 64 === $task->undo_jid, 'undo received parent jid' ); -dm_assert( 2 === $reverted, 'children effects counted as reverted' ); -dm_assert( 0 === $skipped, 'no skipped' ); -dm_assert( 0 === $failed, 'no failed' ); -dm_assert( isset( $jobs_db->stored_engine_data[64]['undo'] ), 'undo metadata stamped on parent' ); -dm_assert( 2 === $jobs_db->stored_engine_data[64]['undo']['effects_reverted'], 'undo metadata records 2 reverted' ); +datamachine_assert( $task->undo_called, 'task->undo() called even though parent has no own effects (the bug fix)' ); +datamachine_assert( 64 === $task->undo_jid, 'undo received parent jid' ); +datamachine_assert( 2 === $reverted, 'children effects counted as reverted' ); +datamachine_assert( 0 === $skipped, 'no skipped' ); +datamachine_assert( 0 === $failed, 'no failed' ); +datamachine_assert( isset( $jobs_db->stored_engine_data[64]['undo'] ), 'undo metadata stamped on parent' ); +datamachine_assert( 2 === $jobs_db->stored_engine_data[64]['undo']['effects_reverted'], 'undo metadata records 2 reverted' ); // ----------------------------------------------------------------- echo "\n[2] true no-op (no own effects, no child effects) — logged + skipped, no engine_data write\n"; @@ -266,10 +266,10 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $parent, $task, $jobs_db, false, false, $reverted, $skipped, $failed ); -dm_assert( $task->undo_called, 'task->undo() still called (no premature short-circuit)' ); -dm_assert( 0 === $reverted && 0 === $failed, 'no reverted, no failed' ); -dm_assert( 1 === $skipped, 'true no-op increments skipped counter' ); -dm_assert( ! isset( $jobs_db->stored_engine_data[70] ), 'no engine_data stamp on a true no-op (no undo metadata write)' ); +datamachine_assert( $task->undo_called, 'task->undo() still called (no premature short-circuit)' ); +datamachine_assert( 0 === $reverted && 0 === $failed, 'no reverted, no failed' ); +datamachine_assert( 1 === $skipped, 'true no-op increments skipped counter' ); +datamachine_assert( ! isset( $jobs_db->stored_engine_data[70] ), 'no engine_data stamp on a true no-op (no undo metadata write)' ); $found_noop_log = false; foreach ( Cli_Capture::$log as $line ) { @@ -278,7 +278,7 @@ function dm_assert( bool $cond, string $msg ): void { break; } } -dm_assert( $found_noop_log, 'no-op surfaced as "no effects to undo" log line' ); +datamachine_assert( $found_noop_log, 'no-op surfaced as "no effects to undo" log line' ); // ----------------------------------------------------------------- echo "\n[3] leaf job with own effects — undo called, normal counter flow\n"; @@ -303,9 +303,9 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $leaf, $task, $jobs_db, false, false, $reverted, $skipped, $failed ); -dm_assert( $task->undo_called, 'leaf undo called' ); -dm_assert( 1 === $reverted, 'leaf effect counted' ); -dm_assert( 0 === $skipped && 0 === $failed, 'no skipped/failed' ); +datamachine_assert( $task->undo_called, 'leaf undo called' ); +datamachine_assert( 1 === $reverted, 'leaf effect counted' ); +datamachine_assert( 0 === $skipped && 0 === $failed, 'no skipped/failed' ); // ----------------------------------------------------------------- echo "\n[4] dry-run on fan-out parent — preview enumerates children's effects\n"; @@ -342,7 +342,7 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $parent, $task, $jobs_db, true, false, $reverted, $skipped, $failed ); -dm_assert( ! $task->undo_called, 'dry-run does NOT call undo' ); +datamachine_assert( ! $task->undo_called, 'dry-run does NOT call undo' ); $found_count_line = false; foreach ( Cli_Capture::$log as $line ) { @@ -351,7 +351,7 @@ function dm_assert( bool $cond, string $msg ): void { break; } } -dm_assert( $found_count_line, 'dry-run preview reports 3 effects (aggregated from 2 children)' ); +datamachine_assert( $found_count_line, 'dry-run preview reports 3 effects (aggregated from 2 children)' ); $found_attachment_line = false; foreach ( Cli_Capture::$log as $line ) { @@ -360,7 +360,7 @@ function dm_assert( bool $cond, string $msg ): void { break; } } -dm_assert( $found_attachment_line, 'dry-run lists per-effect type from child #82' ); +datamachine_assert( $found_attachment_line, 'dry-run lists per-effect type from child #82' ); // ----------------------------------------------------------------- echo "\n[5] dry-run on fan-out parent with no children — logged as no-op, skipped++\n"; @@ -375,8 +375,8 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $parent, $task, $jobs_db, true, false, $reverted, $skipped, $failed ); -dm_assert( ! $task->undo_called, 'dry-run does not call undo' ); -dm_assert( 1 === $skipped, 'dry-run no-op increments skipped' ); +datamachine_assert( ! $task->undo_called, 'dry-run does not call undo' ); +datamachine_assert( 1 === $skipped, 'dry-run no-op increments skipped' ); // ----------------------------------------------------------------- echo "\n[6] dry-run on leaf with own effects — preview reads engine_data['effects'] directly\n"; @@ -396,7 +396,7 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $leaf, $task, $jobs_db, true, false, $reverted, $skipped, $failed ); -dm_assert( ! $task->undo_called, 'dry-run does not call undo' ); +datamachine_assert( ! $task->undo_called, 'dry-run does not call undo' ); $found_count_line = false; foreach ( Cli_Capture::$log as $line ) { if ( str_contains( $line, 'would undo 1 effect(s)' ) ) { @@ -404,7 +404,7 @@ function dm_assert( bool $cond, string $msg ): void { break; } } -dm_assert( $found_count_line, 'dry-run leaf reports 1 effect from own engine_data (children path skipped)' ); +datamachine_assert( $found_count_line, 'dry-run leaf reports 1 effect from own engine_data (children path skipped)' ); // ----------------------------------------------------------------- echo "\n[7] task with already-undone marker — skipped without calling undo\n"; @@ -422,8 +422,8 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $parent, $task, $jobs_db, false, false, $reverted, $skipped, $failed ); -dm_assert( ! $task->undo_called, 'undo NOT called on already-undone parent (no force)' ); -dm_assert( 1 === $skipped, 'already-undone increments skipped' ); +datamachine_assert( ! $task->undo_called, 'undo NOT called on already-undone parent (no force)' ); +datamachine_assert( 1 === $skipped, 'already-undone increments skipped' ); // ----------------------------------------------------------------- echo "\n[8] --force overrides already-undone marker — undo IS called\n"; @@ -439,8 +439,8 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $parent, $task, $jobs_db, false, true, $reverted, $skipped, $failed ); -dm_assert( $task->undo_called, '--force calls undo on already-undone parent' ); -dm_assert( 1 === $reverted, '--force re-undo counts reverted' ); +datamachine_assert( $task->undo_called, '--force calls undo on already-undone parent' ); +datamachine_assert( 1 === $reverted, '--force re-undo counts reverted' ); // ----------------------------------------------------------------- echo "\n[9] envelope with mixed buckets (reverted + skipped + failed)\n"; @@ -460,10 +460,10 @@ function dm_assert( bool $cond, string $msg ): void { run_undo_loop_body( $parent, $task, $jobs_db, false, false, $reverted, $skipped, $failed ); -dm_assert( $task->undo_called, 'undo called' ); -dm_assert( 1 === $reverted, '1 reverted' ); -dm_assert( 1 === $skipped, '1 skipped' ); -dm_assert( 1 === $failed, '1 failed' ); -dm_assert( 1 === count( Cli_Capture::$warning ), 'failed effect surfaces as a CLI warning' ); +datamachine_assert( $task->undo_called, 'undo called' ); +datamachine_assert( 1 === $reverted, '1 reverted' ); +datamachine_assert( 1 === $skipped, '1 skipped' ); +datamachine_assert( 1 === $failed, '1 failed' ); +datamachine_assert( 1 === count( Cli_Capture::$warning ), 'failed effect surfaces as a CLI warning' ); echo "\n=== jobs-command-undo-fanout-smoke: ALL PASS ===\n"; diff --git a/tests/jobs-get-children-smoke.php b/tests/jobs-get-children-smoke.php index 6b85bf3a0..9ff056c3b 100644 --- a/tests/jobs-get-children-smoke.php +++ b/tests/jobs-get-children-smoke.php @@ -51,7 +51,7 @@ function shape_children_rows( array $rows ): array { return $rows; } -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -65,7 +65,7 @@ function dm_assert( bool $cond, string $msg ): void { // ----------------------------------------------------------------- echo "\n[1] empty result set → empty array\n"; $out = shape_children_rows( array() ); -dm_assert( array() === $out, 'empty rows return empty array' ); +datamachine_assert( array() === $out, 'empty rows return empty array' ); // ----------------------------------------------------------------- echo "\n[2] two children, engine_data decoded, order preserved\n"; @@ -86,14 +86,14 @@ function dm_assert( bool $cond, string $msg ): void { $out = shape_children_rows( $rows ); -dm_assert( 2 === count( $out ), 'two rows returned' ); -dm_assert( 65 === $out[0]['job_id'], 'first row is job_id=65 (ASC order preserved)' ); -dm_assert( 66 === $out[1]['job_id'], 'second row is job_id=66 (ASC order preserved)' ); -dm_assert( is_array( $out[0]['engine_data'] ), 'first row engine_data decoded to array' ); -dm_assert( is_array( $out[1]['engine_data'] ), 'second row engine_data decoded to array' ); -dm_assert( 'alt_text' === $out[0]['engine_data']['task_type'], 'task_type accessible after decode' ); -dm_assert( 1 === $out[0]['engine_data']['effects'][0]['target']['post_id'], 'nested effect target accessible' ); -dm_assert( 2 === $out[1]['engine_data']['effects'][0]['target']['post_id'], 'second child distinct from first' ); +datamachine_assert( 2 === count( $out ), 'two rows returned' ); +datamachine_assert( 65 === $out[0]['job_id'], 'first row is job_id=65 (ASC order preserved)' ); +datamachine_assert( 66 === $out[1]['job_id'], 'second row is job_id=66 (ASC order preserved)' ); +datamachine_assert( is_array( $out[0]['engine_data'] ), 'first row engine_data decoded to array' ); +datamachine_assert( is_array( $out[1]['engine_data'] ), 'second row engine_data decoded to array' ); +datamachine_assert( 'alt_text' === $out[0]['engine_data']['task_type'], 'task_type accessible after decode' ); +datamachine_assert( 1 === $out[0]['engine_data']['effects'][0]['target']['post_id'], 'nested effect target accessible' ); +datamachine_assert( 2 === $out[1]['engine_data']['effects'][0]['target']['post_id'], 'second child distinct from first' ); // ----------------------------------------------------------------- echo "\n[3] malformed JSON → engine_data degrades to empty array\n"; @@ -107,8 +107,8 @@ function dm_assert( bool $cond, string $msg ): void { $out = shape_children_rows( $rows ); -dm_assert( 1 === count( $out ), 'one row returned' ); -dm_assert( array() === $out[0]['engine_data'], 'malformed JSON degrades to empty array (not null, not the raw string)' ); +datamachine_assert( 1 === count( $out ), 'one row returned' ); +datamachine_assert( array() === $out[0]['engine_data'], 'malformed JSON degrades to empty array (not null, not the raw string)' ); // ----------------------------------------------------------------- echo "\n[4] empty string engine_data → empty array\n"; @@ -121,7 +121,7 @@ function dm_assert( bool $cond, string $msg ): void { ); $out = shape_children_rows( $rows ); -dm_assert( array() === $out[0]['engine_data'], 'empty string engine_data → empty array' ); +datamachine_assert( array() === $out[0]['engine_data'], 'empty string engine_data → empty array' ); // ----------------------------------------------------------------- echo "\n[5] missing engine_data key → empty array (defensive)\n"; @@ -133,7 +133,7 @@ function dm_assert( bool $cond, string $msg ): void { ); $out = shape_children_rows( $rows ); -dm_assert( array() === $out[0]['engine_data'], 'missing engine_data key → empty array' ); +datamachine_assert( array() === $out[0]['engine_data'], 'missing engine_data key → empty array' ); // ----------------------------------------------------------------- echo "\n[6] mixed children — some with effects, some without (fan-out reality)\n"; @@ -166,10 +166,10 @@ function dm_assert( bool $cond, string $msg ): void { $effects = array_merge( $effects, $child_effects ); } -dm_assert( 3 === count( $out ), 'three children shaped' ); -dm_assert( 3 === count( $effects ), 'merged effects from children = 3 (1 from #80, 0 from #81, 2 from #82)' ); -dm_assert( 'post_meta_set' === $effects[0]['type'], 'first effect from job #80' ); -dm_assert( 'post_field_set' === $effects[1]['type'], 'second effect from job #82 (in order)' ); -dm_assert( 'attachment_created' === $effects[2]['type'], 'third effect from job #82 (in order)' ); +datamachine_assert( 3 === count( $out ), 'three children shaped' ); +datamachine_assert( 3 === count( $effects ), 'merged effects from children = 3 (1 from #80, 0 from #81, 2 from #82)' ); +datamachine_assert( 'post_meta_set' === $effects[0]['type'], 'first effect from job #80' ); +datamachine_assert( 'post_field_set' === $effects[1]['type'], 'second effect from job #82 (in order)' ); +datamachine_assert( 'attachment_created' === $effects[2]['type'], 'third effect from job #82 (in order)' ); echo "\n=== jobs-get-children-smoke: ALL PASS ===\n"; diff --git a/tests/merge-term-meta-smoke.php b/tests/merge-term-meta-smoke.php index 128dae8cc..4f04f10ec 100644 --- a/tests/merge-term-meta-smoke.php +++ b/tests/merge-term-meta-smoke.php @@ -29,7 +29,7 @@ * MergeTermMetaAbility::execute(). Returns ['updated' => string[], * 'skipped' => string[]]. */ -function dm_test_merge_decide( array $data, array $field_map, string $strategy, array $existing_meta ): array { +function datamachine_test_merge_decide( array $data, array $field_map, string $strategy, array $existing_meta ): array { $updated = array(); $skipped = array(); @@ -84,7 +84,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, ); // Case 1: fill_empty, all existing meta empty, all data present → all updated. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'url' => 'https://example.com', 'type' => 'Organization' ), $promoter_field_map, STRATEGY_FILL_EMPTY, @@ -95,7 +95,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, } // Case 2: fill_empty, url already populated → url skipped, type updated. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'url' => 'https://NEW.com', 'type' => 'Person' ), $promoter_field_map, STRATEGY_FILL_EMPTY, @@ -106,7 +106,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, } // Case 3: overwrite, url already populated → url still updated. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'url' => 'https://NEW.com', 'type' => 'Person' ), $promoter_field_map, STRATEGY_OVERWRITE, @@ -117,7 +117,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, } // Case 4: data has no value for a mapped key → skipped. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'url' => 'https://example.com' ), $promoter_field_map, STRATEGY_FILL_EMPTY, @@ -128,7 +128,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, } // Case 5: empty-string incoming → skipped even with overwrite. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'url' => '', 'type' => 'Organization' ), $promoter_field_map, STRATEGY_OVERWRITE, @@ -142,7 +142,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, } // Case 6: keys not in field_map are silently ignored. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'url' => 'https://example.com', 'evil' => 'pwn' ), $promoter_field_map, STRATEGY_FILL_EMPTY, @@ -155,7 +155,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, // Case 7: Venue partial update — only coordinates incoming on a venue with // every address field already populated. fill_empty leaves address alone, // writes coordinates. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'coordinates' => '32.78,-79.93' ), $venue_field_map, STRATEGY_FILL_EMPTY, @@ -170,7 +170,7 @@ function dm_test_merge_decide( array $data, array $field_map, string $strategy, } // Case 8: Venue create-time write (overwrite) populates everything. -$out = dm_test_merge_decide( +$out = datamachine_test_merge_decide( array( 'address' => '123 Main St', 'city' => 'Charleston', diff --git a/tests/meta-description-post-types-filter-smoke.php b/tests/meta-description-post-types-filter-smoke.php index 39f70f7d6..69b882dbf 100644 --- a/tests/meta-description-post-types-filter-smoke.php +++ b/tests/meta-description-post-types-filter-smoke.php @@ -24,23 +24,23 @@ require_once __DIR__ . '/smoke-wp-stubs.php'; // Tiny filter registry — enough for getEligiblePostTypes(). -$GLOBALS['_dm_test_filters'] = array(); +$GLOBALS['_datamachine_test_filters'] = array(); if ( ! function_exists( 'add_filter' ) ) { function add_filter( $hook, $callback, $priority = 10, $accepted_args = 1 ) { - $GLOBALS['_dm_test_filters'][ $hook ][ $priority ][] = $callback; + $GLOBALS['_datamachine_test_filters'][ $hook ][ $priority ][] = $callback; return true; } } if ( ! function_exists( 'remove_filter' ) ) { function remove_filter( $hook, $callback, $priority = 10 ) { - if ( empty( $GLOBALS['_dm_test_filters'][ $hook ][ $priority ] ) ) { + if ( empty( $GLOBALS['_datamachine_test_filters'][ $hook ][ $priority ] ) ) { return false; } - foreach ( $GLOBALS['_dm_test_filters'][ $hook ][ $priority ] as $i => $registered ) { + foreach ( $GLOBALS['_datamachine_test_filters'][ $hook ][ $priority ] as $i => $registered ) { if ( $registered === $callback ) { - unset( $GLOBALS['_dm_test_filters'][ $hook ][ $priority ][ $i ] ); + unset( $GLOBALS['_datamachine_test_filters'][ $hook ][ $priority ][ $i ] ); return true; } } @@ -50,13 +50,13 @@ function remove_filter( $hook, $callback, $priority = 10 ) { if ( ! function_exists( 'apply_filters' ) ) { function apply_filters( $hook, $value ) { - if ( empty( $GLOBALS['_dm_test_filters'][ $hook ] ) ) { + if ( empty( $GLOBALS['_datamachine_test_filters'][ $hook ] ) ) { return $value; } $args = func_get_args(); array_shift( $args ); // drop hook name - ksort( $GLOBALS['_dm_test_filters'][ $hook ] ); - foreach ( $GLOBALS['_dm_test_filters'][ $hook ] as $priority => $callbacks ) { + ksort( $GLOBALS['_datamachine_test_filters'][ $hook ] ); + foreach ( $GLOBALS['_datamachine_test_filters'][ $hook ] as $priority => $callbacks ) { foreach ( $callbacks as $callback ) { $args[0] = call_user_func_array( $callback, $args ); } @@ -77,7 +77,7 @@ function sanitize_key( $key ) { use DataMachine\Abilities\SEO\MetaDescriptionAbilities; -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -88,10 +88,10 @@ function dm_assert( bool $cond, string $msg ): void { echo "getEligiblePostTypes — defaults\n"; $default = MetaDescriptionAbilities::getEligiblePostTypes(); -dm_assert( is_array( $default ), 'returns an array' ); -dm_assert( in_array( 'post', $default, true ), 'includes "post" by default' ); -dm_assert( in_array( 'page', $default, true ), 'includes "page" by default' ); -dm_assert( 2 === count( $default ), 'returns exactly the two defaults' ); +datamachine_assert( is_array( $default ), 'returns an array' ); +datamachine_assert( in_array( 'post', $default, true ), 'includes "post" by default' ); +datamachine_assert( in_array( 'page', $default, true ), 'includes "page" by default' ); +datamachine_assert( 2 === count( $default ), 'returns exactly the two defaults' ); echo "getEligiblePostTypes — filter appends a custom post type\n"; $append_wiki = function ( array $types ): array { @@ -101,18 +101,18 @@ function dm_assert( bool $cond, string $msg ): void { add_filter( 'datamachine_post_types_for_meta_description', $append_wiki ); $with_wiki = MetaDescriptionAbilities::getEligiblePostTypes(); -dm_assert( in_array( 'wiki', $with_wiki, true ), 'includes "wiki" after filter' ); -dm_assert( in_array( 'post', $with_wiki, true ), 'still includes "post"' ); -dm_assert( in_array( 'page', $with_wiki, true ), 'still includes "page"' ); -dm_assert( 3 === count( $with_wiki ), 'returns three post types after filter' ); +datamachine_assert( in_array( 'wiki', $with_wiki, true ), 'includes "wiki" after filter' ); +datamachine_assert( in_array( 'post', $with_wiki, true ), 'still includes "post"' ); +datamachine_assert( in_array( 'page', $with_wiki, true ), 'still includes "page"' ); +datamachine_assert( 3 === count( $with_wiki ), 'returns three post types after filter' ); echo "getEligiblePostTypes — removing the filter restores defaults\n"; remove_filter( 'datamachine_post_types_for_meta_description', $append_wiki ); $restored = MetaDescriptionAbilities::getEligiblePostTypes(); -dm_assert( ! in_array( 'wiki', $restored, true ), '"wiki" gone after remove_filter' ); -dm_assert( in_array( 'post', $restored, true ), '"post" still present' ); -dm_assert( in_array( 'page', $restored, true ), '"page" still present' ); -dm_assert( 2 === count( $restored ), 'returns exactly the two defaults again' ); +datamachine_assert( ! in_array( 'wiki', $restored, true ), '"wiki" gone after remove_filter' ); +datamachine_assert( in_array( 'post', $restored, true ), '"post" still present' ); +datamachine_assert( in_array( 'page', $restored, true ), '"page" still present' ); +datamachine_assert( 2 === count( $restored ), 'returns exactly the two defaults again' ); echo "getEligiblePostTypes — sanitization + dedupe\n"; $noisy = function ( array $_types ): array { @@ -122,12 +122,12 @@ function dm_assert( bool $cond, string $msg ): void { add_filter( 'datamachine_post_types_for_meta_description', $noisy ); $cleaned = MetaDescriptionAbilities::getEligiblePostTypes(); -dm_assert( in_array( 'post', $cleaned, true ), 'lowercases "POST" to "post"' ); -dm_assert( in_array( 'wiki', $cleaned, true ), 'preserves "wiki"' ); -dm_assert( in_array( 'badtype', $cleaned, true ), 'sanitize_key strips invalid chars from "bad type!"' ); -dm_assert( 1 === count( array_filter( $cleaned, fn( $t ) => 'post' === $t ) ), 'dedupes "post"' ); -dm_assert( 1 === count( array_filter( $cleaned, fn( $t ) => 'wiki' === $t ) ), 'dedupes "wiki"' ); -dm_assert( ! in_array( '', $cleaned, true ), 'empty strings filtered out' ); +datamachine_assert( in_array( 'post', $cleaned, true ), 'lowercases "POST" to "post"' ); +datamachine_assert( in_array( 'wiki', $cleaned, true ), 'preserves "wiki"' ); +datamachine_assert( in_array( 'badtype', $cleaned, true ), 'sanitize_key strips invalid chars from "bad type!"' ); +datamachine_assert( 1 === count( array_filter( $cleaned, fn( $t ) => 'post' === $t ) ), 'dedupes "post"' ); +datamachine_assert( 1 === count( array_filter( $cleaned, fn( $t ) => 'wiki' === $t ) ), 'dedupes "wiki"' ); +datamachine_assert( ! in_array( '', $cleaned, true ), 'empty strings filtered out' ); remove_filter( 'datamachine_post_types_for_meta_description', $noisy ); echo "getEligiblePostTypes — non-array return falls back to defaults\n"; @@ -136,8 +136,8 @@ function dm_assert( bool $cond, string $msg ): void { }; add_filter( 'datamachine_post_types_for_meta_description', $broken ); $fallback = MetaDescriptionAbilities::getEligiblePostTypes(); -dm_assert( is_array( $fallback ), 'still returns an array when filter misbehaves' ); -dm_assert( array( 'post', 'page' ) === $fallback, 'falls back to default array exactly' ); +datamachine_assert( is_array( $fallback ), 'still returns an array when filter misbehaves' ); +datamachine_assert( array( 'post', 'page' ) === $fallback, 'falls back to default array exactly' ); remove_filter( 'datamachine_post_types_for_meta_description', $broken ); echo "getEligiblePostTypes — empty filter return falls back to defaults\n"; @@ -146,7 +146,7 @@ function dm_assert( bool $cond, string $msg ): void { }; add_filter( 'datamachine_post_types_for_meta_description', $empty ); $still_default = MetaDescriptionAbilities::getEligiblePostTypes(); -dm_assert( array( 'post', 'page' ) === $still_default, 'empty filter return falls back to defaults' ); +datamachine_assert( array( 'post', 'page' ) === $still_default, 'empty filter return falls back to defaults' ); remove_filter( 'datamachine_post_types_for_meta_description', $empty ); echo "\n[OK] All meta-description post-types filter smoke tests passed.\n"; diff --git a/tests/processed-items-flow-step-parse-smoke.php b/tests/processed-items-flow-step-parse-smoke.php index 56327854c..dabe6f212 100644 --- a/tests/processed-items-flow-step-parse-smoke.php +++ b/tests/processed-items-flow-step-parse-smoke.php @@ -47,7 +47,7 @@ class WP_CLI_Command {} use DataMachine\Cli\Commands\ProcessedItemsCommand; -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -68,27 +68,27 @@ function dm_assert( bool $cond, string $msg ): void { echo "Test 1: canonical {pipeline_id}_{uuid}_{flow_id} shape\n"; $result = $parse( '2_0978b49e-a5a1-46e6-ae3a-0bf62ccea60d_2' ); -dm_assert( is_array( $result ), 'returns an array for canonical input' ); -dm_assert( 2 === $result['pipeline_id'], 'pipeline_id parsed from prefix' ); -dm_assert( 2 === $result['flow_id'], 'flow_id parsed from suffix' ); +datamachine_assert( is_array( $result ), 'returns an array for canonical input' ); +datamachine_assert( 2 === $result['pipeline_id'], 'pipeline_id parsed from prefix' ); +datamachine_assert( 2 === $result['flow_id'], 'flow_id parsed from suffix' ); echo "Test 2: multi-digit pipeline + flow ids\n"; $result = $parse( '42_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_137' ); -dm_assert( 42 === $result['pipeline_id'], 'multi-digit pipeline_id' ); -dm_assert( 137 === $result['flow_id'], 'multi-digit flow_id' ); +datamachine_assert( 42 === $result['pipeline_id'], 'multi-digit pipeline_id' ); +datamachine_assert( 137 === $result['flow_id'], 'multi-digit flow_id' ); echo "Test 3: pipeline_id and flow_id can match without aliasing the uuid\n"; $result = $parse( '7_xxxx-yyyy-zzzz_7' ); -dm_assert( 7 === $result['pipeline_id'], 'leading 7' ); -dm_assert( 7 === $result['flow_id'], 'trailing 7 — different segment' ); +datamachine_assert( 7 === $result['pipeline_id'], 'leading 7' ); +datamachine_assert( 7 === $result['flow_id'], 'trailing 7 — different segment' ); echo "Test 4: malformed inputs return null\n"; -dm_assert( null === $parse( '' ), 'empty string' ); -dm_assert( null === $parse( '2' ), 'no underscore' ); -dm_assert( null === $parse( '2_uuid' ), 'single underscore (no flow_id segment)' ); -dm_assert( null === $parse( 'abc_uuid_2' ), 'non-numeric pipeline_id' ); -dm_assert( null === $parse( '2_uuid_abc' ), 'non-numeric flow_id' ); -dm_assert( null === $parse( '_uuid_2' ), 'empty pipeline_id segment' ); -dm_assert( null === $parse( '2_uuid_' ), 'empty flow_id segment' ); +datamachine_assert( null === $parse( '' ), 'empty string' ); +datamachine_assert( null === $parse( '2' ), 'no underscore' ); +datamachine_assert( null === $parse( '2_uuid' ), 'single underscore (no flow_id segment)' ); +datamachine_assert( null === $parse( 'abc_uuid_2' ), 'non-numeric pipeline_id' ); +datamachine_assert( null === $parse( '2_uuid_abc' ), 'non-numeric flow_id' ); +datamachine_assert( null === $parse( '_uuid_2' ), 'empty pipeline_id segment' ); +datamachine_assert( null === $parse( '2_uuid_' ), 'empty flow_id segment' ); echo "\nAll smoke checks passed.\n"; diff --git a/tests/processed-items-source-rejected-cli-smoke.php b/tests/processed-items-source-rejected-cli-smoke.php index 0d335f00f..51de59cad 100644 --- a/tests/processed-items-source-rejected-cli-smoke.php +++ b/tests/processed-items-source-rejected-cli-smoke.php @@ -40,7 +40,7 @@ class WP_CLI_Command {} use DataMachine\Cli\Commands\ProcessedItemsCommand; -function dm_source_rejected_assert( bool $cond, string $msg ): void { +function datamachine_source_rejected_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -59,9 +59,9 @@ function dm_source_rejected_assert( bool $cond, string $msg ): void { echo "Test 1: default query targets processed rows from source-rejected jobs\n"; $result = $query_parts->invoke( $cmd, array(), 'wp_datamachine_processed_items', 'wp_datamachine_jobs' ); -dm_source_rejected_assert( str_contains( $result['where_sql'], 'pi.status = %s' ), 'filters processed item status' ); -dm_source_rejected_assert( str_contains( $result['where_sql'], 'j.status = %s' ), 'filters owning job status' ); -dm_source_rejected_assert( +datamachine_source_rejected_assert( str_contains( $result['where_sql'], 'pi.status = %s' ), 'filters processed item status' ); +datamachine_source_rejected_assert( str_contains( $result['where_sql'], 'j.status = %s' ), 'filters owning job status' ); +datamachine_source_rejected_assert( array( 'wp_datamachine_processed_items', 'wp_datamachine_jobs', 'processed', 'agent_skipped - source-rejected' ) === $result['values'], 'default values include both table names and exact source-rejected status' ); @@ -79,12 +79,12 @@ function dm_source_rejected_assert( bool $cond, string $msg ): void { 'wp_datamachine_processed_items', 'wp_datamachine_jobs' ); -dm_source_rejected_assert( str_contains( $result['where_sql'], 'j.pipeline_id = %s' ), 'adds pipeline filter' ); -dm_source_rejected_assert( str_contains( $result['where_sql'], 'j.flow_id = %s' ), 'adds flow filter' ); -dm_source_rejected_assert( str_contains( $result['where_sql'], 'pi.source_type = %s' ), 'adds source type filter' ); -dm_source_rejected_assert( str_contains( $result['where_sql'], 'pi.processed_timestamp >= %s' ), 'adds lower date bound' ); -dm_source_rejected_assert( str_contains( $result['where_sql'], 'pi.processed_timestamp <= %s' ), 'adds upper date bound' ); -dm_source_rejected_assert( in_array( 'mcp', $result['values'], true ), 'keeps source type generic' ); +datamachine_source_rejected_assert( str_contains( $result['where_sql'], 'j.pipeline_id = %s' ), 'adds pipeline filter' ); +datamachine_source_rejected_assert( str_contains( $result['where_sql'], 'j.flow_id = %s' ), 'adds flow filter' ); +datamachine_source_rejected_assert( str_contains( $result['where_sql'], 'pi.source_type = %s' ), 'adds source type filter' ); +datamachine_source_rejected_assert( str_contains( $result['where_sql'], 'pi.processed_timestamp >= %s' ), 'adds lower date bound' ); +datamachine_source_rejected_assert( str_contains( $result['where_sql'], 'pi.processed_timestamp <= %s' ), 'adds upper date bound' ); +datamachine_source_rejected_assert( in_array( 'mcp', $result['values'], true ), 'keeps source type generic' ); echo "Test 3: clear output describes blast-radius filters\n"; $description = $describe->invoke( @@ -95,8 +95,8 @@ function dm_source_rejected_assert( bool $cond, string $msg ): void { 'after' => '2026-05-01', ) ); -dm_source_rejected_assert( str_contains( $description, 'job-status=agent_skipped - source-rejected' ), 'describes default job status' ); -dm_source_rejected_assert( str_contains( $description, 'pipeline=12' ), 'describes pipeline scope' ); -dm_source_rejected_assert( str_contains( $description, 'source-type=mcp' ), 'describes source-type scope' ); +datamachine_source_rejected_assert( str_contains( $description, 'job-status=agent_skipped - source-rejected' ), 'describes default job status' ); +datamachine_source_rejected_assert( str_contains( $description, 'pipeline=12' ), 'describes pipeline scope' ); +datamachine_source_rejected_assert( str_contains( $description, 'source-type=mcp' ), 'describes source-type scope' ); echo "\nAll source-rejected CLI smoke checks passed.\n"; diff --git a/tests/queueable-trait-smoke.php b/tests/queueable-trait-smoke.php index b52518f80..832df8614 100644 --- a/tests/queueable-trait-smoke.php +++ b/tests/queueable-trait-smoke.php @@ -47,7 +47,7 @@ public function publicMerge( array $config, array $patch ): array { } } -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -67,7 +67,7 @@ function dm_assert( bool $cond, string $msg ): void { 'provider' => 'mgs', 'params' => '{"query":"WooCommerce"}', ); -dm_assert( +datamachine_assert( $cfg === $harness->publicMerge( $cfg, array() ), 'config returned unchanged' ); @@ -89,8 +89,8 @@ function dm_assert( bool $cond, string $msg ): void { $merged = $harness->publicMerge( $cfg, $patch ); $decoded = json_decode( $merged['params'], true ); -dm_assert( 'a8c' === $merged['server'], 'unrelated keys preserved' ); -dm_assert( +datamachine_assert( 'a8c' === $merged['server'], 'unrelated keys preserved' ); +datamachine_assert( array( 'query' => 'WooCommerce', 'after' => '2017-03-01', @@ -98,7 +98,7 @@ function dm_assert( bool $cond, string $msg ): void { ) === $decoded, 'patch keys merged into decoded params, original keys preserved' ); -dm_assert( is_string( $merged['params'] ), 'params remains a JSON string after merge' ); +datamachine_assert( is_string( $merged['params'] ), 'params remains a JSON string after merge' ); // ----------------------------------------------------------------- echo "\n[3] queued patch wins on key collision\n"; @@ -107,8 +107,8 @@ function dm_assert( bool $cond, string $msg ): void { $merged = $harness->publicMerge( $cfg, $patch ); $decoded = json_decode( $merged['params'], true ); -dm_assert( '2020-01-01' === $decoded['after'], 'queued value wins on collision' ); -dm_assert( 'WooCommerce' === $decoded['query'], 'non-conflicting key preserved' ); +datamachine_assert( '2020-01-01' === $decoded['after'], 'queued value wins on collision' ); +datamachine_assert( 'WooCommerce' === $decoded['query'], 'non-conflicting key preserved' ); // ----------------------------------------------------------------- echo "\n[4] top-level scalar in patch overlays config\n"; @@ -119,8 +119,8 @@ function dm_assert( bool $cond, string $msg ): void { $patch = array( 'max_items' => 50 ); $merged = $harness->publicMerge( $cfg, $patch ); -dm_assert( 50 === $merged['max_items'], 'scalar overlay applied' ); -dm_assert( 'a8c' === $merged['server'], 'unrelated scalar preserved' ); +datamachine_assert( 50 === $merged['max_items'], 'scalar overlay applied' ); +datamachine_assert( 'a8c' === $merged['server'], 'unrelated scalar preserved' ); // ----------------------------------------------------------------- echo "\n[5] new key in patch added to config\n"; @@ -128,8 +128,8 @@ function dm_assert( bool $cond, string $msg ): void { $patch = array( 'after' => '2017-03-01' ); $merged = $harness->publicMerge( $cfg, $patch ); -dm_assert( '2017-03-01' === $merged['after'], 'new key added' ); -dm_assert( 'a8c' === $merged['server'], 'existing key preserved' ); +datamachine_assert( '2017-03-01' === $merged['after'], 'new key added' ); +datamachine_assert( 'a8c' === $merged['server'], 'existing key preserved' ); // ----------------------------------------------------------------- echo "\n[6] assoc-array values deep-merge\n"; @@ -147,7 +147,7 @@ function dm_assert( bool $cond, string $msg ): void { ); $merged = $harness->publicMerge( $cfg, $patch ); -dm_assert( +datamachine_assert( array( 'timeout' => 30, 'retries' => 5, @@ -162,7 +162,7 @@ function dm_assert( bool $cond, string $msg ): void { $patch = array( 'tags' => array( '2026-04', 'fse-checkout' ) ); $merged = $harness->publicMerge( $cfg, $patch ); -dm_assert( +datamachine_assert( array( 'wc', 'release', '2026-04', 'fse-checkout' ) === $merged['tags'], 'numeric array concatenated rather than replaced' ); @@ -173,7 +173,7 @@ function dm_assert( bool $cond, string $msg ): void { $patch = array( 'params' => array( 'after' => '2017-03-01' ) ); $merged = $harness->publicMerge( $cfg, $patch ); -dm_assert( +datamachine_assert( array( 'after' => '2017-03-01' ) === $merged['params'], 'array patch wins outright when string field is unparseable' ); @@ -184,7 +184,7 @@ function dm_assert( bool $cond, string $msg ): void { $patch = array( 'tool' => 'fetch_posts' ); $merged = $harness->publicMerge( $cfg, $patch ); -dm_assert( 'fetch_posts' === $merged['tool'], 'string-to-string replacement' ); +datamachine_assert( 'fetch_posts' === $merged['tool'], 'string-to-string replacement' ); // ----------------------------------------------------------------- echo "\n[10] WooCommerce backfill real-world shape end-to-end\n"; @@ -204,12 +204,12 @@ function dm_assert( bool $cond, string $msg ): void { ); $merged = $harness->publicMerge( $static_config, $queued_patch ); -dm_assert( 'a8c' === $merged['server'], 'server preserved' ); -dm_assert( 'mgs' === $merged['provider'], 'provider preserved' ); -dm_assert( 'search' === $merged['tool'], 'tool preserved' ); -dm_assert( 20 === $merged['max_items'], 'max_items preserved' ); -dm_assert( is_string( $merged['params'] ), 'params remains JSON string for handler' ); -dm_assert( +datamachine_assert( 'a8c' === $merged['server'], 'server preserved' ); +datamachine_assert( 'mgs' === $merged['provider'], 'provider preserved' ); +datamachine_assert( 'search' === $merged['tool'], 'tool preserved' ); +datamachine_assert( 20 === $merged['max_items'], 'max_items preserved' ); +datamachine_assert( is_string( $merged['params'] ), 'params remains JSON string for handler' ); +datamachine_assert( array( 'query' => 'WooCommerce', 'after' => '2017-03-01', @@ -223,7 +223,7 @@ function dm_assert( bool $cond, string $msg ): void { echo "\n[11] empty config + non-empty patch — patch becomes the config\n"; $merged = $harness->publicMerge( array(), array( 'after' => '2015-01-01', 'before' => '2015-02-01' ) ); -dm_assert( +datamachine_assert( array( 'after' => '2015-01-01', 'before' => '2015-02-01', @@ -233,7 +233,7 @@ function dm_assert( bool $cond, string $msg ): void { // ----------------------------------------------------------------- echo "\n[12] both empty\n"; -dm_assert( +datamachine_assert( array() === $harness->publicMerge( array(), array() ), 'empty + empty = empty' ); @@ -260,19 +260,19 @@ function dm_assert( bool $cond, string $msg ): void { ); $merged = $harness->publicMerge( $static_config, $wrong_shape ); -dm_assert( +datamachine_assert( '{"query":"WooCommerce"}' === $merged['params'], 'JSON-encoded params untouched when patch keys do not match the params key' ); -dm_assert( 'WooCommerce' === $merged['query'], 'flat patch key landed at top level (where handler will not read it)' ); -dm_assert( '2026-04-01' === $merged['after'], 'flat after lands at top level' ); -dm_assert( '2026-04-25' === $merged['before'], 'flat before lands at top level' ); +datamachine_assert( 'WooCommerce' === $merged['query'], 'flat patch key landed at top level (where handler will not read it)' ); +datamachine_assert( '2026-04-01' === $merged['after'], 'flat after lands at top level' ); +datamachine_assert( '2026-04-25' === $merged['before'], 'flat before lands at top level' ); // merged_keys log: comparing patch_keys ["query","after","before"] to // merged_keys ["server","provider","tool","params","query","after","before"] // surfaces the mis-shaping — the keys are present but alongside `params` // rather than inside it. $expected_merged_keys = array( 'server', 'provider', 'tool', 'params', 'query', 'after', 'before' ); -dm_assert( +datamachine_assert( $expected_merged_keys === array_keys( $merged ), 'merged_keys log surfaces the mis-shaping: patch keys sit alongside params, not inside it' ); diff --git a/tests/recurring-rejection-escalation-smoke.php b/tests/recurring-rejection-escalation-smoke.php index dda795ee6..cd1182bc4 100644 --- a/tests/recurring-rejection-escalation-smoke.php +++ b/tests/recurring-rejection-escalation-smoke.php @@ -32,23 +32,23 @@ // ─── In-memory WordPress option + hook stubs ──────────────────────── -$GLOBALS['dm_test_options'] = array(); -$GLOBALS['dm_test_logs'] = array(); +$GLOBALS['datamachine_test_options'] = array(); +$GLOBALS['datamachine_test_logs'] = array(); if ( ! function_exists( 'get_option' ) ) { function get_option( string $name, $default = false ) { - return $GLOBALS['dm_test_options'][ $name ] ?? $default; + return $GLOBALS['datamachine_test_options'][ $name ] ?? $default; } } if ( ! function_exists( 'update_option' ) ) { function update_option( string $name, $value, $autoload = null ): bool { - $GLOBALS['dm_test_options'][ $name ] = $value; + $GLOBALS['datamachine_test_options'][ $name ] = $value; return true; } } if ( ! function_exists( 'delete_option' ) ) { function delete_option( string $name ): bool { - unset( $GLOBALS['dm_test_options'][ $name ] ); + unset( $GLOBALS['datamachine_test_options'][ $name ] ); return true; } } @@ -60,7 +60,7 @@ function apply_filters( string $hook, $value, ...$args ) { if ( ! function_exists( 'do_action' ) ) { function do_action( string $hook, ...$args ): void { if ( 'datamachine_log' === $hook ) { - $GLOBALS['dm_test_logs'][] = array( + $GLOBALS['datamachine_test_logs'][] = array( 'level' => $args[0] ?? '', 'message' => $args[1] ?? '', 'context' => $args[2] ?? array(), @@ -89,14 +89,14 @@ function do_action( string $hook, ...$args ): void { }; $reset_state = function (): void { - $GLOBALS['dm_test_options'] = array(); - $GLOBALS['dm_test_logs'] = array(); + $GLOBALS['datamachine_test_options'] = array(); + $GLOBALS['datamachine_test_logs'] = array(); }; $escalation_logs = static function (): array { return array_values( array_filter( - $GLOBALS['dm_test_logs'], + $GLOBALS['datamachine_test_logs'], static fn( array $log ): bool => ( $log['context']['error_code'] ?? '' ) === RecurringRejectionTracker::ESCALATION_ERROR_CODE ) @@ -148,7 +148,7 @@ function do_action( string $hook, ...$args ): void { $assert( 'tracker state for schedule is fully cleared', ! isset( RecurringRejectionTracker::all()['workspace_disk_emergency_cleanup'] ) ); echo "\n[c2] After reset, a fresh rejection streak escalates again\n"; -$GLOBALS['dm_test_logs'] = array(); +$GLOBALS['datamachine_test_logs'] = array(); for ( $i = 1; $i <= $threshold; $i++ ) { RecurringRejectionTracker::record_rejection( 'workspace_disk_emergency_cleanup', 'workspace_disk_emergency_cleanup', 'task_scheduler_agent_context_required' ); } diff --git a/tests/recurring-scheduler-idempotency-smoke.php b/tests/recurring-scheduler-idempotency-smoke.php index c88be3af9..1506796ab 100644 --- a/tests/recurring-scheduler-idempotency-smoke.php +++ b/tests/recurring-scheduler-idempotency-smoke.php @@ -142,13 +142,13 @@ public function get_schedule(): DmRecurringSchedulerFakeSchedule { } } -$GLOBALS['dm_rs_actions'] = array(); -$GLOBALS['dm_rs_scheduled_single'] = 0; -$GLOBALS['dm_rs_scheduled_recurring'] = 0; -$GLOBALS['dm_rs_scheduled_cron'] = 0; -$GLOBALS['dm_rs_unscheduled'] = 0; +$GLOBALS['datamachine_rs_actions'] = array(); +$GLOBALS['datamachine_rs_scheduled_single'] = 0; +$GLOBALS['datamachine_rs_scheduled_recurring'] = 0; +$GLOBALS['datamachine_rs_scheduled_cron'] = 0; +$GLOBALS['datamachine_rs_unscheduled'] = 0; -function dm_rs_key( string $hook, array $args, string $group ): string { +function datamachine_rs_key( string $hook, array $args, string $group ): string { return $group . '|' . $hook . '|' . serialize( $args ); } @@ -158,35 +158,35 @@ function dm_rs_key( string $hook, array $args, string $group ): string { * The store keeps a LIST per signature so the harness can model the live * duplicate-chain bug (two parallel pending actions for one hook/args/group). */ -function dm_rs_seed_action( string $hook, array $args, string $group, DmRecurringSchedulerFakeSchedule $schedule ): void { - $key = dm_rs_key( $hook, $args, $group ); - if ( ! isset( $GLOBALS['dm_rs_actions'][ $key ] ) ) { - $GLOBALS['dm_rs_actions'][ $key ] = array(); +function datamachine_rs_seed_action( string $hook, array $args, string $group, DmRecurringSchedulerFakeSchedule $schedule ): void { + $key = datamachine_rs_key( $hook, $args, $group ); + if ( ! isset( $GLOBALS['datamachine_rs_actions'][ $key ] ) ) { + $GLOBALS['datamachine_rs_actions'][ $key ] = array(); } - $GLOBALS['dm_rs_actions'][ $key ][] = new DmRecurringSchedulerFakeAction( $schedule ); + $GLOBALS['datamachine_rs_actions'][ $key ][] = new DmRecurringSchedulerFakeAction( $schedule ); } -function dm_rs_pending_count( string $hook, array $args, string $group ): int { - $key = dm_rs_key( $hook, $args, $group ); - return isset( $GLOBALS['dm_rs_actions'][ $key ] ) ? count( $GLOBALS['dm_rs_actions'][ $key ] ) : 0; +function datamachine_rs_pending_count( string $hook, array $args, string $group ): int { + $key = datamachine_rs_key( $hook, $args, $group ); + return isset( $GLOBALS['datamachine_rs_actions'][ $key ] ) ? count( $GLOBALS['datamachine_rs_actions'][ $key ] ) : 0; } -function dm_rs_reset(): void { - $GLOBALS['dm_rs_actions'] = array(); - $GLOBALS['dm_rs_scheduled_single'] = 0; - $GLOBALS['dm_rs_scheduled_recurring'] = 0; - $GLOBALS['dm_rs_scheduled_cron'] = 0; - $GLOBALS['dm_rs_unscheduled'] = 0; +function datamachine_rs_reset(): void { + $GLOBALS['datamachine_rs_actions'] = array(); + $GLOBALS['datamachine_rs_scheduled_single'] = 0; + $GLOBALS['datamachine_rs_scheduled_recurring'] = 0; + $GLOBALS['datamachine_rs_scheduled_cron'] = 0; + $GLOBALS['datamachine_rs_unscheduled'] = 0; ActionScheduler::$initialized = true; } -function dm_rs_counter( string $key ): int { +function datamachine_rs_counter( string $key ): int { return (int) ( $GLOBALS[ $key ] ?? 0 ); } function as_get_scheduled_actions( array $args = array(), $return_format = OBJECT ): array { - $key = dm_rs_key( $args['hook'], $args['args'] ?? array(), $args['group'] ?? '' ); - $matches = $GLOBALS['dm_rs_actions'][ $key ] ?? array(); + $key = datamachine_rs_key( $args['hook'], $args['args'] ?? array(), $args['group'] ?? '' ); + $matches = $GLOBALS['datamachine_rs_actions'][ $key ] ?? array(); $per_page = isset( $args['per_page'] ) ? (int) $args['per_page'] : count( $matches ); if ( $per_page > 0 ) { $matches = array_slice( $matches, 0, $per_page ); @@ -200,35 +200,35 @@ function as_get_scheduled_actions( array $args = array(), $return_format = OBJEC } function as_next_scheduled_action( string $hook, ?array $args = null, string $group = '' ) { - $key = dm_rs_key( $hook, $args ?? array(), $group ); - if ( empty( $GLOBALS['dm_rs_actions'][ $key ] ) ) { + $key = datamachine_rs_key( $hook, $args ?? array(), $group ); + if ( empty( $GLOBALS['datamachine_rs_actions'][ $key ] ) ) { return false; } - $first = reset( $GLOBALS['dm_rs_actions'][ $key ] ); + $first = reset( $GLOBALS['datamachine_rs_actions'][ $key ] ); return $first->get_schedule()->get_date()->getTimestamp(); } function as_unschedule_all_actions( string $hook, array $args = array(), string $group = '' ): void { - ++$GLOBALS['dm_rs_unscheduled']; - unset( $GLOBALS['dm_rs_actions'][ dm_rs_key( $hook, $args, $group ) ] ); + ++$GLOBALS['datamachine_rs_unscheduled']; + unset( $GLOBALS['datamachine_rs_actions'][ datamachine_rs_key( $hook, $args, $group ) ] ); } function as_schedule_single_action( int $timestamp, string $hook, array $args = array(), string $group = '' ): int { - ++$GLOBALS['dm_rs_scheduled_single']; - dm_rs_seed_action( $hook, $args, $group, new DmRecurringSchedulerFakeSchedule( false, null, $timestamp ) ); + ++$GLOBALS['datamachine_rs_scheduled_single']; + datamachine_rs_seed_action( $hook, $args, $group, new DmRecurringSchedulerFakeSchedule( false, null, $timestamp ) ); return 101; } function as_schedule_recurring_action( int $timestamp, int $interval, string $hook, array $args = array(), string $group = '' ): int { - ++$GLOBALS['dm_rs_scheduled_recurring']; - dm_rs_seed_action( $hook, $args, $group, new DmRecurringSchedulerFakeSchedule( true, $interval, $timestamp ) ); + ++$GLOBALS['datamachine_rs_scheduled_recurring']; + datamachine_rs_seed_action( $hook, $args, $group, new DmRecurringSchedulerFakeSchedule( true, $interval, $timestamp ) ); return 202; } function as_schedule_cron_action( int $timestamp, string $expression, string $hook, array $args = array(), string $group = '' ): int { - ++$GLOBALS['dm_rs_scheduled_cron']; - dm_rs_seed_action( $hook, $args, $group, new DmRecurringSchedulerFakeSchedule( true, $expression, $timestamp ) ); + ++$GLOBALS['datamachine_rs_scheduled_cron']; + datamachine_rs_seed_action( $hook, $args, $group, new DmRecurringSchedulerFakeSchedule( true, $expression, $timestamp ) ); return 303; } @@ -236,7 +236,7 @@ function as_schedule_cron_action( int $timestamp, string $expression, string $ho use DataMachine\Engine\Tasks\RecurringScheduler; -function dm_assert( bool $condition, string $message ): void { +function datamachine_assert( bool $condition, string $message ): void { if ( $condition ) { echo " [PASS] {$message}\n"; return; @@ -246,129 +246,129 @@ function dm_assert( bool $condition, string $message ): void { exit( 1 ); } -function dm_assert_schedule_result( $result ): array { - dm_assert( is_array( $result ), 'scheduler returned result array' ); +function datamachine_assert_schedule_result( $result ): array { + datamachine_assert( is_array( $result ), 'scheduler returned result array' ); return $result; } echo "=== recurring-scheduler-idempotency-smoke ===\n"; echo "\n[1] matching recurring schedule is preserved\n"; -dm_rs_reset(); -dm_rs_seed_action( 'dm_hook', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 86400, time() + 600 ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_hook', array(), 'daily' ) ); -dm_assert( true === ( $result['preserved'] ?? false ), 'result marks preserved recurring action' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_unscheduled' ), 'matching recurring action was not unscheduled' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_scheduled_recurring' ), 'matching recurring action was not recreated' ); +datamachine_rs_reset(); +datamachine_rs_seed_action( 'datamachine_hook', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 86400, time() + 600 ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_hook', array(), 'daily' ) ); +datamachine_assert( true === ( $result['preserved'] ?? false ), 'result marks preserved recurring action' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'matching recurring action was not unscheduled' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_scheduled_recurring' ), 'matching recurring action was not recreated' ); echo "\n[2] changed recurring interval is replaced\n"; -dm_rs_reset(); -dm_rs_seed_action( 'dm_hook', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 86400, time() + 600 ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_hook', array(), 'hourly' ) ); -dm_assert( empty( $result['preserved'] ), 'changed recurring action is not marked preserved' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_unscheduled' ), 'changed recurring action was unscheduled' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_recurring' ), 'changed recurring action was recreated' ); -dm_assert( 3600 === $result['interval_seconds'], 'new recurring interval is applied' ); +datamachine_rs_reset(); +datamachine_rs_seed_action( 'datamachine_hook', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 86400, time() + 600 ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_hook', array(), 'hourly' ) ); +datamachine_assert( empty( $result['preserved'] ), 'changed recurring action is not marked preserved' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'changed recurring action was unscheduled' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_recurring' ), 'changed recurring action was recreated' ); +datamachine_assert( 3600 === $result['interval_seconds'], 'new recurring interval is applied' ); echo "\n[3] force_reschedule replaces an otherwise matching recurring schedule\n"; -dm_rs_reset(); -dm_rs_seed_action( 'dm_hook', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 86400, time() + 600 ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_hook', array(), 'daily', array( 'force_reschedule' => true ) ) ); -dm_assert( empty( $result['preserved'] ), 'forced recurring action is not marked preserved' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_unscheduled' ), 'forced recurring action was unscheduled' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_recurring' ), 'forced recurring action was recreated' ); +datamachine_rs_reset(); +datamachine_rs_seed_action( 'datamachine_hook', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 86400, time() + 600 ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_hook', array(), 'daily', array( 'force_reschedule' => true ) ) ); +datamachine_assert( empty( $result['preserved'] ), 'forced recurring action is not marked preserved' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'forced recurring action was unscheduled' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_recurring' ), 'forced recurring action was recreated' ); echo "\n[4] matching cron schedule is preserved\n"; -dm_rs_reset(); -dm_rs_seed_action( 'dm_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 600 ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_cron', array(), 'cron', array( 'cron_expression' => '0 0 * * *' ) ) ); -dm_assert( true === ( $result['preserved'] ?? false ), 'result marks preserved cron action' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_unscheduled' ), 'matching cron action was not unscheduled' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_scheduled_cron' ), 'matching cron action was not recreated' ); +datamachine_rs_reset(); +datamachine_rs_seed_action( 'datamachine_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 600 ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_cron', array(), 'cron', array( 'cron_expression' => '0 0 * * *' ) ) ); +datamachine_assert( true === ( $result['preserved'] ?? false ), 'result marks preserved cron action' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'matching cron action was not unscheduled' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_scheduled_cron' ), 'matching cron action was not recreated' ); echo "\n[5] changed cron expression is replaced\n"; -dm_rs_reset(); -dm_rs_seed_action( 'dm_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 600 ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_cron', array(), 'cron', array( 'cron_expression' => '15 0 * * *' ) ) ); -dm_assert( empty( $result['preserved'] ), 'changed cron action is not marked preserved' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_unscheduled' ), 'changed cron action was unscheduled' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_cron' ), 'changed cron action was recreated' ); +datamachine_rs_reset(); +datamachine_rs_seed_action( 'datamachine_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 600 ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_cron', array(), 'cron', array( 'cron_expression' => '15 0 * * *' ) ) ); +datamachine_assert( empty( $result['preserved'] ), 'changed cron action is not marked preserved' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'changed cron action was unscheduled' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_cron' ), 'changed cron action was recreated' ); echo "\n[6] matching one-time timestamp is preserved\n"; -dm_rs_reset(); +datamachine_rs_reset(); $timestamp = time() + 3600; -dm_rs_seed_action( 'dm_once', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( false, null, $timestamp ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_once', array(), 'one_time', array( 'timestamp' => $timestamp ) ) ); -dm_assert( true === ( $result['preserved'] ?? false ), 'result marks preserved one-time action' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_unscheduled' ), 'matching one-time action was not unscheduled' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_scheduled_single' ), 'matching one-time action was not recreated' ); +datamachine_rs_seed_action( 'datamachine_once', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( false, null, $timestamp ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_once', array(), 'one_time', array( 'timestamp' => $timestamp ) ) ); +datamachine_assert( true === ( $result['preserved'] ?? false ), 'result marks preserved one-time action' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'matching one-time action was not unscheduled' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_scheduled_single' ), 'matching one-time action was not recreated' ); echo "\n[7] changed one-time timestamp is replaced\n"; -dm_rs_reset(); -dm_rs_seed_action( 'dm_once', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( false, null, $timestamp ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_once', array(), 'one_time', array( 'timestamp' => $timestamp + 60 ) ) ); -dm_assert( empty( $result['preserved'] ), 'changed one-time action is not marked preserved' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_unscheduled' ), 'changed one-time action was unscheduled' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_single' ), 'changed one-time action was recreated' ); +datamachine_rs_reset(); +datamachine_rs_seed_action( 'datamachine_once', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( false, null, $timestamp ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_once', array(), 'one_time', array( 'timestamp' => $timestamp + 60 ) ) ); +datamachine_assert( empty( $result['preserved'] ), 'changed one-time action is not marked preserved' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'changed one-time action was unscheduled' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_single' ), 'changed one-time action was recreated' ); echo "\n[8] calling ensureSchedule twice yields exactly ONE recurring chain (#2512)\n"; -dm_rs_reset(); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_dup', array(), 'hourly' ) ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_recurring' ), 'first call creates one recurring chain' ); -dm_assert( 1 === dm_rs_pending_count( 'dm_dup', array(), RecurringScheduler::GROUP ), 'one pending action after first call' ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_dup', array(), 'hourly' ) ); -dm_assert( true === ( $result['preserved'] ?? false ), 'second call preserves the single matching chain' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_recurring' ), 'second call does NOT create a second chain' ); -dm_assert( 1 === dm_rs_pending_count( 'dm_dup', array(), RecurringScheduler::GROUP ), 'still exactly one pending action after second call' ); +datamachine_rs_reset(); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_dup', array(), 'hourly' ) ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_recurring' ), 'first call creates one recurring chain' ); +datamachine_assert( 1 === datamachine_rs_pending_count( 'datamachine_dup', array(), RecurringScheduler::GROUP ), 'one pending action after first call' ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_dup', array(), 'hourly' ) ); +datamachine_assert( true === ( $result['preserved'] ?? false ), 'second call preserves the single matching chain' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_recurring' ), 'second call does NOT create a second chain' ); +datamachine_assert( 1 === datamachine_rs_pending_count( 'datamachine_dup', array(), RecurringScheduler::GROUP ), 'still exactly one pending action after second call' ); echo "\n[9] datastore-not-ready bails instead of creating a duplicate recurring chain (#2512)\n"; -dm_rs_reset(); +datamachine_rs_reset(); // Existing live chain (created earlier when AS was ready). -dm_rs_seed_action( 'dm_dup', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 3600, time() + 600 ) ); +datamachine_rs_seed_action( 'datamachine_dup', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 3600, time() + 600 ) ); ActionScheduler::$initialized = false; // AS datastore reports NOT ready. -$result = RecurringScheduler::ensureSchedule( 'dm_dup', array(), 'hourly' ); -dm_assert( $result instanceof WP_Error, 'not-ready datastore returns WP_Error' ); -dm_assert( 'datastore_not_ready' === $result->get_error_code(), 'error code is datastore_not_ready' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_scheduled_recurring' ), 'no new recurring chain created while AS not ready' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_unscheduled' ), 'no unschedule attempted while AS not ready' ); -dm_assert( 1 === dm_rs_pending_count( 'dm_dup', array(), RecurringScheduler::GROUP ), 'existing chain left intact — still exactly one' ); +$result = RecurringScheduler::ensureSchedule( 'datamachine_dup', array(), 'hourly' ); +datamachine_assert( $result instanceof WP_Error, 'not-ready datastore returns WP_Error' ); +datamachine_assert( 'datastore_not_ready' === $result->get_error_code(), 'error code is datastore_not_ready' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_scheduled_recurring' ), 'no new recurring chain created while AS not ready' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'no unschedule attempted while AS not ready' ); +datamachine_assert( 1 === datamachine_rs_pending_count( 'datamachine_dup', array(), RecurringScheduler::GROUP ), 'existing chain left intact — still exactly one' ); echo "\n[10] an already-duplicated recurring signature self-heals to one chain (#2512)\n"; -dm_rs_reset(); +datamachine_rs_reset(); // Simulate the live bug: two parallel pending chains for the same signature. -dm_rs_seed_action( 'dm_dup', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 3600, time() + 600 ) ); -dm_rs_seed_action( 'dm_dup', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 3600, time() + 700 ) ); -dm_assert( 2 === dm_rs_pending_count( 'dm_dup', array(), RecurringScheduler::GROUP ), 'two duplicate chains seeded' ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_dup', array(), 'hourly' ) ); -dm_assert( true === ( $result['deduplicated'] ?? false ), 'result marks deduplicated' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_unscheduled' ), 'duplicates were unscheduled' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_recurring' ), 'exactly one chain recreated' ); -dm_assert( 1 === dm_rs_pending_count( 'dm_dup', array(), RecurringScheduler::GROUP ), 'collapsed to exactly one pending action' ); +datamachine_rs_seed_action( 'datamachine_dup', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 3600, time() + 600 ) ); +datamachine_rs_seed_action( 'datamachine_dup', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, 3600, time() + 700 ) ); +datamachine_assert( 2 === datamachine_rs_pending_count( 'datamachine_dup', array(), RecurringScheduler::GROUP ), 'two duplicate chains seeded' ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_dup', array(), 'hourly' ) ); +datamachine_assert( true === ( $result['deduplicated'] ?? false ), 'result marks deduplicated' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'duplicates were unscheduled' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_recurring' ), 'exactly one chain recreated' ); +datamachine_assert( 1 === datamachine_rs_pending_count( 'datamachine_dup', array(), RecurringScheduler::GROUP ), 'collapsed to exactly one pending action' ); echo "\n[11] cron branch also bails when datastore not ready (#2512)\n"; -dm_rs_reset(); +datamachine_rs_reset(); ActionScheduler::$initialized = false; -$result = RecurringScheduler::ensureSchedule( 'dm_cron', array(), 'cron', array( 'cron_expression' => '0 0 * * *' ) ); -dm_assert( $result instanceof WP_Error, 'cron not-ready datastore returns WP_Error' ); -dm_assert( 'datastore_not_ready' === $result->get_error_code(), 'cron error code is datastore_not_ready' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_scheduled_cron' ), 'no cron chain created while AS not ready' ); +$result = RecurringScheduler::ensureSchedule( 'datamachine_cron', array(), 'cron', array( 'cron_expression' => '0 0 * * *' ) ); +datamachine_assert( $result instanceof WP_Error, 'cron not-ready datastore returns WP_Error' ); +datamachine_assert( 'datastore_not_ready' === $result->get_error_code(), 'cron error code is datastore_not_ready' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_scheduled_cron' ), 'no cron chain created while AS not ready' ); echo "\n[12] one_time branch also bails when datastore not ready (#2512)\n"; -dm_rs_reset(); +datamachine_rs_reset(); ActionScheduler::$initialized = false; -$result = RecurringScheduler::ensureSchedule( 'dm_once', array(), 'one_time', array( 'timestamp' => time() + 3600 ) ); -dm_assert( $result instanceof WP_Error, 'one_time not-ready datastore returns WP_Error' ); -dm_assert( 'datastore_not_ready' === $result->get_error_code(), 'one_time error code is datastore_not_ready' ); -dm_assert( 0 === dm_rs_counter( 'dm_rs_scheduled_single' ), 'no one_time action created while AS not ready' ); +$result = RecurringScheduler::ensureSchedule( 'datamachine_once', array(), 'one_time', array( 'timestamp' => time() + 3600 ) ); +datamachine_assert( $result instanceof WP_Error, 'one_time not-ready datastore returns WP_Error' ); +datamachine_assert( 'datastore_not_ready' === $result->get_error_code(), 'one_time error code is datastore_not_ready' ); +datamachine_assert( 0 === datamachine_rs_counter( 'datamachine_rs_scheduled_single' ), 'no one_time action created while AS not ready' ); echo "\n[13] an already-duplicated cron signature self-heals to one chain (#2512)\n"; -dm_rs_reset(); -dm_rs_seed_action( 'dm_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 600 ) ); -dm_rs_seed_action( 'dm_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 700 ) ); -$result = dm_assert_schedule_result( RecurringScheduler::ensureSchedule( 'dm_cron', array(), 'cron', array( 'cron_expression' => '0 0 * * *' ) ) ); -dm_assert( true === ( $result['deduplicated'] ?? false ), 'cron result marks deduplicated' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_unscheduled' ), 'cron duplicates were unscheduled' ); -dm_assert( 1 === dm_rs_counter( 'dm_rs_scheduled_cron' ), 'exactly one cron chain recreated' ); -dm_assert( 1 === dm_rs_pending_count( 'dm_cron', array(), RecurringScheduler::GROUP ), 'cron collapsed to exactly one pending action' ); +datamachine_rs_reset(); +datamachine_rs_seed_action( 'datamachine_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 600 ) ); +datamachine_rs_seed_action( 'datamachine_cron', array(), RecurringScheduler::GROUP, new DmRecurringSchedulerFakeSchedule( true, '0 0 * * *', time() + 700 ) ); +$result = datamachine_assert_schedule_result( RecurringScheduler::ensureSchedule( 'datamachine_cron', array(), 'cron', array( 'cron_expression' => '0 0 * * *' ) ) ); +datamachine_assert( true === ( $result['deduplicated'] ?? false ), 'cron result marks deduplicated' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_unscheduled' ), 'cron duplicates were unscheduled' ); +datamachine_assert( 1 === datamachine_rs_counter( 'datamachine_rs_scheduled_cron' ), 'exactly one cron chain recreated' ); +datamachine_assert( 1 === datamachine_rs_pending_count( 'datamachine_cron', array(), RecurringScheduler::GROUP ), 'cron collapsed to exactly one pending action' ); echo "\nAll recurring scheduler idempotency assertions passed.\n"; diff --git a/tests/resolve-term-args-smoke.php b/tests/resolve-term-args-smoke.php index 451c34614..12d942f7a 100644 --- a/tests/resolve-term-args-smoke.php +++ b/tests/resolve-term-args-smoke.php @@ -35,20 +35,20 @@ * @param array $args Raw caller args. * @return array Sanitised args ready for wp_insert_term(). */ -function dm_test_normalize_term_args( array $args ): array { +function datamachine_test_normalize_term_args( array $args ): array { $clean = array(); if ( isset( $args['description'] ) ) { - $clean['description'] = dm_test_sanitize_textarea_field( (string) $args['description'] ); + $clean['description'] = datamachine_test_sanitize_textarea_field( (string) $args['description'] ); } if ( isset( $args['parent'] ) ) { - $clean['parent'] = dm_test_absint( $args['parent'] ); + $clean['parent'] = datamachine_test_absint( $args['parent'] ); } if ( isset( $args['slug'] ) ) { - $clean['slug'] = dm_test_sanitize_title( (string) $args['slug'] ); + $clean['slug'] = datamachine_test_sanitize_title( (string) $args['slug'] ); } if ( isset( $args['alias_of'] ) ) { - $clean['alias_of'] = dm_test_sanitize_title( (string) $args['alias_of'] ); + $clean['alias_of'] = datamachine_test_sanitize_title( (string) $args['alias_of'] ); } return $clean; @@ -57,16 +57,16 @@ function dm_test_normalize_term_args( array $args ): array { // Lightweight stand-ins for the WordPress sanitisers used by the production // code. Behaviour is approximated, not byte-identical — the production code // runs the real WordPress functions. -function dm_test_sanitize_textarea_field( string $value ): string { +function datamachine_test_sanitize_textarea_field( string $value ): string { $value = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $value ); return trim( $value ); } -function dm_test_absint( $value ): int { +function datamachine_test_absint( $value ): int { return abs( (int) $value ); } -function dm_test_sanitize_title( string $value ): string { +function datamachine_test_sanitize_title( string $value ): string { $value = strtolower( trim( $value ) ); $value = preg_replace( '/[^a-z0-9\-_]+/', '-', $value ); $value = trim( $value, '-' ); @@ -76,13 +76,13 @@ function dm_test_sanitize_title( string $value ): string { $failures = array(); // Case 1: empty input => empty array. -$out = dm_test_normalize_term_args( array() ); +$out = datamachine_test_normalize_term_args( array() ); if ( $out !== array() ) { $failures[] = 'empty input should yield empty array, got: ' . var_export( $out, true ); } // Case 2: all four whitelisted keys flow through. -$out = dm_test_normalize_term_args( +$out = datamachine_test_normalize_term_args( array( 'description' => 'A description', 'parent' => '12', @@ -104,7 +104,7 @@ function dm_test_sanitize_title( string $value ): string { } // Case 3: unknown keys are silently dropped (no leakage to wp_insert_term). -$out = dm_test_normalize_term_args( +$out = datamachine_test_normalize_term_args( array( 'description' => 'desc', 'evil' => 'should not survive', @@ -116,13 +116,13 @@ function dm_test_sanitize_title( string $value ): string { } // Case 4: parent=0 is preserved (top-level), not dropped. -$out = dm_test_normalize_term_args( array( 'parent' => 0 ) ); +$out = datamachine_test_normalize_term_args( array( 'parent' => 0 ) ); if ( ! array_key_exists( 'parent', $out ) || 0 !== $out['parent'] ) { $failures[] = 'parent=0 should pass through as 0: ' . var_export( $out, true ); } // Case 5: negative parent is absint'd. -$out = dm_test_normalize_term_args( array( 'parent' => -5 ) ); +$out = datamachine_test_normalize_term_args( array( 'parent' => -5 ) ); if ( 5 !== $out['parent'] ) { $failures[] = 'negative parent not absint\'d: ' . var_export( $out, true ); } diff --git a/tests/run-metrics-smoke.php b/tests/run-metrics-smoke.php index 6c021ebb1..c04775ee6 100644 --- a/tests/run-metrics-smoke.php +++ b/tests/run-metrics-smoke.php @@ -25,7 +25,7 @@ function wp_json_encode( $data, $flags = 0, $depth = 512 ) { use DataMachine\Core\RunMetrics; -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -47,10 +47,10 @@ function dm_assert( bool $cond, string $msg ): void { ) ); -dm_assert( 4 === $normalized['counts']['processed'], 'processed count preserved' ); -dm_assert( 2 === $normalized['counts']['staged_actions'], 'staged action count preserved' ); -dm_assert( 0 === $normalized['counts']['failed'], 'missing failed count defaults to zero' ); -dm_assert( 0 === $normalized['counts']['retried'], 'missing retried count defaults to zero' ); +datamachine_assert( 4 === $normalized['counts']['processed'], 'processed count preserved' ); +datamachine_assert( 2 === $normalized['counts']['staged_actions'], 'staged action count preserved' ); +datamachine_assert( 0 === $normalized['counts']['failed'], 'missing failed count defaults to zero' ); +datamachine_assert( 0 === $normalized['counts']['retried'], 'missing retried count defaults to zero' ); echo "\n[2] fromJob combines persisted metrics, batch results, tokens, and duration\n"; $metrics = RunMetrics::fromJob( @@ -85,16 +85,16 @@ function dm_assert( bool $cond, string $msg ): void { ) ); -dm_assert( 123 === $metrics['job_id'], 'job_id surfaced' ); -dm_assert( 11 === $metrics['counts']['selected'], 'batch selected count surfaced' ); -dm_assert( 8 === $metrics['counts']['processed'], 'batch completed count maps to processed' ); -dm_assert( 2 === $metrics['counts']['skipped'], 'batch skipped count surfaced' ); -dm_assert( 1 === $metrics['counts']['failed'], 'batch failed count surfaced' ); -dm_assert( 1 === $metrics['counts']['retried'], 'batch retried count surfaced' ); -dm_assert( 3 === $metrics['counts']['staged_actions'], 'staged action count surfaced' ); -dm_assert( 2 === $metrics['counts']['accepted_actions'], 'accepted action count surfaced' ); -dm_assert( 300 === $metrics['duration_seconds'], 'duration uses started/completed timestamps' ); -dm_assert( 99 === $metrics['token_usage']['total_tokens'], 'token usage is exposed when present' ); +datamachine_assert( 123 === $metrics['job_id'], 'job_id surfaced' ); +datamachine_assert( 11 === $metrics['counts']['selected'], 'batch selected count surfaced' ); +datamachine_assert( 8 === $metrics['counts']['processed'], 'batch completed count maps to processed' ); +datamachine_assert( 2 === $metrics['counts']['skipped'], 'batch skipped count surfaced' ); +datamachine_assert( 1 === $metrics['counts']['failed'], 'batch failed count surfaced' ); +datamachine_assert( 1 === $metrics['counts']['retried'], 'batch retried count surfaced' ); +datamachine_assert( 3 === $metrics['counts']['staged_actions'], 'staged action count surfaced' ); +datamachine_assert( 2 === $metrics['counts']['accepted_actions'], 'accepted action count surfaced' ); +datamachine_assert( 300 === $metrics['duration_seconds'], 'duration uses started/completed timestamps' ); +datamachine_assert( 99 === $metrics['token_usage']['total_tokens'], 'token usage is exposed when present' ); echo "\n[3] status-derived counts cover terminal no-item and failed jobs\n"; $skipped = RunMetrics::fromJob( @@ -116,8 +116,8 @@ function dm_assert( bool $cond, string $msg ): void { ) ); -dm_assert( 1 === $skipped['counts']['skipped'], 'completed_no_items increments skipped' ); -dm_assert( 1 === $failed['counts']['failed'], 'failed status increments failed' ); +datamachine_assert( 1 === $skipped['counts']['skipped'], 'completed_no_items increments skipped' ); +datamachine_assert( 1 === $failed['counts']['failed'], 'failed status increments failed' ); echo "\n[4] engine_data writers use compare-and-swap, not blind overwrite (regression: #2762)\n"; // The lost-update race behind batch_state_missing came from start(), increment(), @@ -126,7 +126,7 @@ function dm_assert( bool $cond, string $msg ): void { // All three must route through the compare-and-swap EngineData::mutate() path so // they can never clobber another writer's keys. $run_metrics_source = file_get_contents( __DIR__ . '/../inc/Core/RunMetrics.php' ); -dm_assert( false !== $run_metrics_source, 'RunMetrics source readable' ); +datamachine_assert( false !== $run_metrics_source, 'RunMetrics source readable' ); foreach ( array( 'start', 'increment', 'complete' ) as $method ) { if ( ! preg_match( '/public static function ' . $method . '\([^)]*\)[^{]*\{(.*?)\n\t\}/s', $run_metrics_source, $m ) ) { @@ -134,8 +134,8 @@ function dm_assert( bool $cond, string $msg ): void { exit( 1 ); } $body = $m[1]; - dm_assert( str_contains( $body, 'EngineData::mutate' ), "{$method}() persists via EngineData::mutate (CAS)" ); - dm_assert( ! str_contains( $body, 'EngineData::persist' ), "{$method}() does not blind-overwrite via EngineData::persist" ); + datamachine_assert( str_contains( $body, 'EngineData::mutate' ), "{$method}() persists via EngineData::mutate (CAS)" ); + datamachine_assert( ! str_contains( $body, 'EngineData::persist' ), "{$method}() does not blind-overwrite via EngineData::persist" ); } echo "\n=== run-metrics-smoke: ALL PASS ===\n"; diff --git a/tests/run-state-smoke.php b/tests/run-state-smoke.php index d54aea4f3..39160be58 100644 --- a/tests/run-state-smoke.php +++ b/tests/run-state-smoke.php @@ -20,7 +20,7 @@ use DataMachine\Core\RunState; -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -44,15 +44,15 @@ function dm_assert( bool $cond, string $msg ): void { 'cancelled', ); -dm_assert( $expected_states === RunState::ALL_STATES, 'all states are ordered and complete' ); -dm_assert( count( $expected_states ) === count( array_unique( RunState::ALL_STATES ) ), 'states are unique' ); +datamachine_assert( $expected_states === RunState::ALL_STATES, 'all states are ordered and complete' ); +datamachine_assert( count( $expected_states ) === count( array_unique( RunState::ALL_STATES ) ), 'states are unique' ); echo "\n[2] validity is exact, not prefix-based\n"; foreach ( $expected_states as $state ) { - dm_assert( RunState::is_valid( $state ), "{$state} is valid" ); + datamachine_assert( RunState::is_valid( $state ), "{$state} is valid" ); } -dm_assert( ! RunState::is_valid( 'waiting' ), 'generic waiting is intentionally not a run state' ); -dm_assert( ! RunState::is_valid( 'waiting_for_human' ), 'unregistered future wait reason is invalid until named' ); +datamachine_assert( ! RunState::is_valid( 'waiting' ), 'generic waiting is intentionally not a run state' ); +datamachine_assert( ! RunState::is_valid( 'waiting_for_human' ), 'unregistered future wait reason is invalid until named' ); echo "\n[3] waiting states are explicit pause reasons\n"; $expected_waiting = array( @@ -61,21 +61,21 @@ function dm_assert( bool $cond, string $msg ): void { 'waiting_for_approval', 'waiting_for_callback', ); -dm_assert( $expected_waiting === RunState::WAITING_STATES, 'waiting states are explicit reason states' ); +datamachine_assert( $expected_waiting === RunState::WAITING_STATES, 'waiting states are explicit reason states' ); foreach ( $expected_waiting as $state ) { - dm_assert( RunState::is_waiting( $state ), "{$state} is waiting" ); + datamachine_assert( RunState::is_waiting( $state ), "{$state} is waiting" ); } -dm_assert( ! RunState::is_waiting( RunState::PENDING ), 'pending is not waiting' ); -dm_assert( ! RunState::is_waiting( RunState::RUNNING ), 'running is not waiting' ); -dm_assert( ! RunState::is_waiting( RunState::COMPLETED ), 'completed is not waiting' ); +datamachine_assert( ! RunState::is_waiting( RunState::PENDING ), 'pending is not waiting' ); +datamachine_assert( ! RunState::is_waiting( RunState::RUNNING ), 'running is not waiting' ); +datamachine_assert( ! RunState::is_waiting( RunState::COMPLETED ), 'completed is not waiting' ); echo "\n[4] terminal states are distinct from resumable states\n"; $expected_terminal = array( 'completed', 'failed', 'cancelled' ); -dm_assert( $expected_terminal === RunState::TERMINAL_STATES, 'terminal states are complete' ); +datamachine_assert( $expected_terminal === RunState::TERMINAL_STATES, 'terminal states are complete' ); foreach ( $expected_terminal as $state ) { - dm_assert( RunState::is_terminal( $state ), "{$state} is terminal" ); + datamachine_assert( RunState::is_terminal( $state ), "{$state} is terminal" ); } -dm_assert( ! RunState::is_terminal( RunState::WAITING_FOR_CALLBACK ), 'waiting_for_callback remains resumable' ); -dm_assert( ! RunState::is_terminal( RunState::RUNNING ), 'running is not terminal' ); +datamachine_assert( ! RunState::is_terminal( RunState::WAITING_FOR_CALLBACK ), 'waiting_for_callback remains resumable' ); +datamachine_assert( ! RunState::is_terminal( RunState::RUNNING ), 'running is not terminal' ); echo "\n=== run-state-smoke: ALL PASS ===\n"; diff --git a/tests/system-task-workflow-validation-smoke.php b/tests/system-task-workflow-validation-smoke.php index 9db12f78a..d7d74a339 100644 --- a/tests/system-task-workflow-validation-smoke.php +++ b/tests/system-task-workflow-validation-smoke.php @@ -84,7 +84,7 @@ function validate_workflow_for_test( array $workflow ): array { return array( 'valid' => true ); } -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; } echo " [FAIL] {$msg}\n"; exit( 1 ); @@ -107,19 +107,19 @@ function dm_assert( bool $cond, string $msg ): void { ), ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'system_task step accepted without handler_slug' ); +datamachine_assert( true === $r['valid'], 'system_task step accepted without handler_slug' ); // ----------------------------------------------------------------- echo "\n[2] webhook_gate step with no handler_slug — VALID\n"; $wf = array( 'steps' => array( array( 'step_type' => 'webhook_gate' ) ) ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'webhook_gate step accepted without handler_slug' ); +datamachine_assert( true === $r['valid'], 'webhook_gate step accepted without handler_slug' ); // ----------------------------------------------------------------- echo "\n[3] ai step with no handler_slug — VALID (existing behavior)\n"; $wf = array( 'steps' => array( array( 'step_type' => 'ai' ) ) ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'ai step accepted without handler_slug' ); +datamachine_assert( true === $r['valid'], 'ai step accepted without handler_slug' ); // ----------------------------------------------------------------- echo "\n[4] fetch step without handler_slug — VALID at workflow level\n"; @@ -129,26 +129,26 @@ function dm_assert( bool $cond, string $msg ): void { // validate their own config. $wf = array( 'steps' => array( array( 'step_type' => 'fetch' ) ) ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'fetch without handler_slug accepted at workflow level' ); +datamachine_assert( true === $r['valid'], 'fetch without handler_slug accepted at workflow level' ); // ----------------------------------------------------------------- echo "\n[5] fetch step WITH legacy handler_slug — INVALID\n"; $wf = array( 'steps' => array( array( 'step_type' => 'fetch', 'handler_slug' => 'rss' ) ) ); $r = validate_workflow_for_test( $wf ); -dm_assert( false === $r['valid'], 'fetch with legacy handler_slug rejected' ); -dm_assert( str_contains( $r['error'] ?? '', 'unsupported legacy field handler_slug' ), 'legacy handler_slug error is explicit' ); +datamachine_assert( false === $r['valid'], 'fetch with legacy handler_slug rejected' ); +datamachine_assert( str_contains( $r['error'] ?? '', 'unsupported legacy field handler_slug' ), 'legacy handler_slug error is explicit' ); // ----------------------------------------------------------------- echo "\n[6] publish step without handler_slug — VALID at workflow level\n"; $wf = array( 'steps' => array( array( 'step_type' => 'publish' ) ) ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'publish without handler_slug accepted at workflow level' ); +datamachine_assert( true === $r['valid'], 'publish without handler_slug accepted at workflow level' ); // ----------------------------------------------------------------- echo "\n[7] upsert step without handler_slug — VALID at workflow level\n"; $wf = array( 'steps' => array( array( 'step_type' => 'upsert' ) ) ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'upsert without handler_slug accepted at workflow level' ); +datamachine_assert( true === $r['valid'], 'upsert without handler_slug accepted at workflow level' ); // ----------------------------------------------------------------- echo "\n[8] mixed workflow: fetch + ai + system_task — VALID\n"; @@ -164,33 +164,33 @@ function dm_assert( bool $cond, string $msg ): void { ), ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'realistic mixed workflow valid' ); +datamachine_assert( true === $r['valid'], 'realistic mixed workflow valid' ); // ----------------------------------------------------------------- echo "\n[9] step with unregistered type — INVALID\n"; $wf = array( 'steps' => array( array( 'step_type' => 'time_travel' ) ) ); $r = validate_workflow_for_test( $wf ); -dm_assert( false === $r['valid'], 'unregistered type rejected' ); -dm_assert( str_contains( $r['error'], 'invalid step_type' ), 'error names the issue' ); -dm_assert( str_contains( $r['error'], 'time_travel' ), 'error names the bad type' ); -dm_assert( str_contains( $r['error'], 'Valid types' ), 'error lists valid alternatives' ); +datamachine_assert( false === $r['valid'], 'unregistered type rejected' ); +datamachine_assert( str_contains( $r['error'], 'invalid step_type' ), 'error names the issue' ); +datamachine_assert( str_contains( $r['error'], 'time_travel' ), 'error names the bad type' ); +datamachine_assert( str_contains( $r['error'], 'Valid types' ), 'error lists valid alternatives' ); // ----------------------------------------------------------------- echo "\n[10] empty workflow — INVALID\n"; $r = validate_workflow_for_test( array( 'steps' => array() ) ); -dm_assert( false === $r['valid'], 'empty workflow rejected' ); +datamachine_assert( false === $r['valid'], 'empty workflow rejected' ); // ----------------------------------------------------------------- echo "\n[11] workflow without steps array — INVALID\n"; $r = validate_workflow_for_test( array() ); -dm_assert( false === $r['valid'], 'workflow lacking steps array rejected' ); +datamachine_assert( false === $r['valid'], 'workflow lacking steps array rejected' ); // ----------------------------------------------------------------- echo "\n[12] step missing step_type — INVALID\n"; $r = validate_workflow_for_test( array( 'steps' => array( array() ) ) ); -dm_assert( false === $r['valid'], 'step without step_type rejected' ); -dm_assert( str_contains( $r['error'], 'missing step_type' ), 'error names the issue' ); -dm_assert( str_contains( $r['error'], 'Step 0' ), 'error identifies the step index' ); +datamachine_assert( false === $r['valid'], 'step without step_type rejected' ); +datamachine_assert( str_contains( $r['error'], 'missing step_type' ), 'error names the issue' ); +datamachine_assert( str_contains( $r['error'], 'Step 0' ), 'error identifies the step index' ); // ----------------------------------------------------------------- echo "\n[13] real-world DailyMemoryTask workflow — VALID (the bug case)\n"; @@ -207,6 +207,6 @@ function dm_assert( bool $cond, string $msg ): void { ), ); $r = validate_workflow_for_test( $wf ); -dm_assert( true === $r['valid'], 'DailyMemoryTask workflow validates cleanly' ); +datamachine_assert( true === $r['valid'], 'DailyMemoryTask workflow validates cleanly' ); echo "\n=== system-task-workflow-validation-smoke: ALL PASS ===\n"; diff --git a/tests/task-scheduler-batch-parent-link-smoke.php b/tests/task-scheduler-batch-parent-link-smoke.php index 0574f1793..d8edb42c6 100644 --- a/tests/task-scheduler-batch-parent-link-smoke.php +++ b/tests/task-scheduler-batch-parent-link-smoke.php @@ -86,7 +86,7 @@ function build_execute_workflow_create_args( ?array $initial_data ): array { return $args; } -function dm_assert( bool $cond, string $msg ): void { +function datamachine_assert( bool $cond, string $msg ): void { if ( $cond ) { echo " [PASS] {$msg}\n"; return; @@ -105,39 +105,39 @@ function dm_assert( bool $cond, string $msg ): void { 'user_id' => 1, ); $resolved = resolve_small_batch_parent_id( $context ); -dm_assert( 64 === $resolved, 'small-batch path resolves caller parent_job_id from context' ); +datamachine_assert( 64 === $resolved, 'small-batch path resolves caller parent_job_id from context' ); // ----------------------------------------------------------------- echo "\n[2] small batch without parent_job_id → resolves to 0\n"; $context = array( 'agent_id' => 2 ); $resolved = resolve_small_batch_parent_id( $context ); -dm_assert( 0 === $resolved, 'no parent_job_id in context → 0 (no link)' ); +datamachine_assert( 0 === $resolved, 'no parent_job_id in context → 0 (no link)' ); // ----------------------------------------------------------------- echo "\n[3] small batch with parent_job_id=0 explicit → 0 (no link)\n"; $context = array( 'parent_job_id' => 0 ); $resolved = resolve_small_batch_parent_id( $context ); -dm_assert( 0 === $resolved, 'explicit parent_job_id=0 stays 0' ); +datamachine_assert( 0 === $resolved, 'explicit parent_job_id=0 stays 0' ); // ----------------------------------------------------------------- echo "\n[4] small batch with string parent_job_id → coerced to int\n"; $context = array( 'parent_job_id' => '99' ); $resolved = resolve_small_batch_parent_id( $context ); -dm_assert( 99 === $resolved, 'string parent_job_id coerced to int' ); +datamachine_assert( 99 === $resolved, 'string parent_job_id coerced to int' ); // ----------------------------------------------------------------- echo "\n[5] large batch parent — caller passed parent_job_id → batch parent links to caller\n"; $args = build_batch_parent_create_args( 'wiki_maintain', 64 ); -dm_assert( isset( $args['parent_job_id'] ), 'parent_job_id stamped onto batch parent create_args' ); -dm_assert( 64 === $args['parent_job_id'], 'batch parent linked to caller (job 64)' ); -dm_assert( 'batch' === $args['source'], 'source still batch' ); -dm_assert( 'Batch: Wiki maintain' === $args['label'], 'label humanized from task_type' ); +datamachine_assert( isset( $args['parent_job_id'] ), 'parent_job_id stamped onto batch parent create_args' ); +datamachine_assert( 64 === $args['parent_job_id'], 'batch parent linked to caller (job 64)' ); +datamachine_assert( 'batch' === $args['source'], 'source still batch' ); +datamachine_assert( 'Batch: Wiki maintain' === $args['label'], 'label humanized from task_type' ); // ----------------------------------------------------------------- echo "\n[6] large batch parent — no caller parent → no parent_job_id key\n"; $args = build_batch_parent_create_args( 'alt_text', 0 ); -dm_assert( ! isset( $args['parent_job_id'] ), 'no parent_job_id key when caller did not pass one' ); -dm_assert( 'batch' === $args['source'], 'source still batch' ); +datamachine_assert( ! isset( $args['parent_job_id'] ), 'no parent_job_id key when caller did not pass one' ); +datamachine_assert( 'batch' === $args['source'], 'source still batch' ); // ----------------------------------------------------------------- echo "\n[7] large batch chunk — caller_parent_job_id wins over batch_parent_id\n"; @@ -150,7 +150,7 @@ function dm_assert( bool $cond, string $msg ): void { 'caller_parent_job_id' => 64, ); $resolved = resolve_chunk_child_parent_id( $extra, 100 ); // 100 = batch_parent_id -dm_assert( 64 === $resolved, 'children chain to caller (64), not batch parent (100)' ); +datamachine_assert( 64 === $resolved, 'children chain to caller (64), not batch parent (100)' ); // ----------------------------------------------------------------- echo "\n[8] large batch chunk — no caller parent → children chain to batch_parent\n"; @@ -159,7 +159,7 @@ function dm_assert( bool $cond, string $msg ): void { 'caller_parent_job_id' => 0, ); $resolved = resolve_chunk_child_parent_id( $extra, 100 ); -dm_assert( 100 === $resolved, 'children chain to batch parent when caller did not pass parent_job_id' ); +datamachine_assert( 100 === $resolved, 'children chain to batch parent when caller did not pass parent_job_id' ); // ----------------------------------------------------------------- echo "\n[9] large batch chunk — missing caller_parent_job_id key → falls back to batch_parent\n"; @@ -168,7 +168,7 @@ function dm_assert( bool $cond, string $msg ): void { // no caller_parent_job_id key at all. ); $resolved = resolve_chunk_child_parent_id( $extra, 100 ); -dm_assert( 100 === $resolved, 'missing caller_parent_job_id key → batch parent' ); +datamachine_assert( 100 === $resolved, 'missing caller_parent_job_id key → batch parent' ); // ----------------------------------------------------------------- echo "\n[10] ExecuteWorkflowAbility — parent_job_id from initial_data → create_args\n"; @@ -182,18 +182,18 @@ function dm_assert( bool $cond, string $msg ): void { 'user_id' => 1, ); $args = build_execute_workflow_create_args( $initial_data ); -dm_assert( isset( $args['parent_job_id'] ), 'parent_job_id stamped onto create_args from initial_data' ); -dm_assert( 64 === $args['parent_job_id'], 'create_args parent_job_id = 64' ); +datamachine_assert( isset( $args['parent_job_id'] ), 'parent_job_id stamped onto create_args from initial_data' ); +datamachine_assert( 64 === $args['parent_job_id'], 'create_args parent_job_id = 64' ); // ----------------------------------------------------------------- echo "\n[11] ExecuteWorkflowAbility — no parent_job_id in initial_data → no key\n"; $args = build_execute_workflow_create_args( array( 'task_type' => 'wiki_maintain' ) ); -dm_assert( ! isset( $args['parent_job_id'] ), 'no parent_job_id key when initial_data lacks it' ); +datamachine_assert( ! isset( $args['parent_job_id'] ), 'no parent_job_id key when initial_data lacks it' ); // ----------------------------------------------------------------- echo "\n[12] ExecuteWorkflowAbility — null initial_data → no key\n"; $args = build_execute_workflow_create_args( null ); -dm_assert( ! isset( $args['parent_job_id'] ), 'null initial_data → no parent_job_id key' ); +datamachine_assert( ! isset( $args['parent_job_id'] ), 'null initial_data → no parent_job_id key' ); // ----------------------------------------------------------------- echo "\n[13] end-to-end fan-out shape — caller(64) → 3 children, all linked\n"; @@ -221,6 +221,6 @@ function dm_assert( bool $cond, string $msg ): void { $child_link_ids[] = $args['parent_job_id'] ?? 0; } -dm_assert( array( 64, 64, 64 ) === $child_link_ids, 'all three children linked to caller (64)' ); +datamachine_assert( array( 64, 64, 64 ) === $child_link_ids, 'all three children linked to caller (64)' ); echo "\n=== task-scheduler-batch-parent-link-smoke: ALL PASS ===\n";