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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions tests/Unit/Api/Flows/FlowsEndpointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,40 @@ public function test_get_flows_for_pipeline(): void {
$this->assertGreaterThan(0, count($data['data']['flows']));
}

public function test_repeated_rest_creation_preserves_distinct_sibling_flows(): void {
$created_ids = array();

foreach ( array( 'First REST sibling', 'Second REST sibling' ) as $flow_name ) {
$request = new WP_REST_Request( 'POST', '/datamachine/v1/flows' );
$request->set_body_params(
array(
'pipeline_id' => $this->test_pipeline_id,
'flow_name' => $flow_name,
)
);

$response = rest_do_request( $request );
$data = $response->get_data();

$this->assertSame( 200, $response->get_status() );
$this->assertTrue( $data['success'] );
$this->assertArrayNotHasKey( 'code', $data );
$created_ids[] = (int) $data['data']['flow_id'];
}

$this->assertCount( 2, array_unique( $created_ids ) );

$request = new WP_REST_Request( 'GET', '/datamachine/v1/flows' );
$request->set_param( 'pipeline_id', $this->test_pipeline_id );
$response = rest_do_request( $request );
$data = $response->get_data();
$flow_ids = array_map( 'intval', array_column( $data['data']['flows'], 'flow_id' ) );

$this->assertSame( 200, $response->get_status() );
$this->assertTrue( $data['success'] );
$this->assertEmpty( array_diff( $created_ids, $flow_ids ) );
}

public function test_pagination(): void {
$request = new WP_REST_Request('GET', '/datamachine/v1/flows');
$request->set_param('pipeline_id', $this->test_pipeline_id);
Expand Down
6 changes: 3 additions & 3 deletions tests/fuzz/pipeline-builder-runtime.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@
},
{
"id": "flow-sibling-preservation",
"description": "A case-local pipeline creates two sibling flows and preserves their list after reload, replaying the current flow creation failure.",
"metadata": { "seed": 321904, "issue": 3254, "dimensions": ["flow-create", "flow-sibling-preservation", "reload", "known-product-defect-replay"] },
"description": "A case-local pipeline creates two distinct sibling flows through successful REST requests and preserves both after reload.",
"metadata": { "seed": 321904, "issue": 3254, "dimensions": ["flow-create", "rest-success", "distinct-flow-ids", "flow-sibling-preservation", "reload", "regression"] },
"phases": {
"setup": [
{ "command": "wordpress.ensure-plugin-active", "args": ["plugin=data-machine/data-machine.php"] },
{ "command": "wp-codebox.checkpoint-create", "args": ["name=flow-sibling-preservation"] },
{ "command": "wordpress.browser-actions", "args": ["auth=wordpress-admin", "step-timeout=30s", "steps-json=[{\"kind\":\"navigate\",\"url\":\"/wp-admin/admin.php?page=datamachine-pipelines\"},{\"kind\":\"waitFor\",\"selector\":\".datamachine-pipelines-layout\"},{\"kind\":\"click\",\"text\":\"Add New Pipeline\"},{\"kind\":\"waitFor\",\"selector\":\".datamachine-flow-card--empty button\"}]", "capture=steps,console,errors"] }
],
"action": [{ "command": "wordpress.browser-actions", "args": ["auth=wordpress-admin", "step-timeout=30s", "steps-json=[{\"kind\":\"navigate\",\"url\":\"/wp-admin/admin.php?page=datamachine-pipelines\"},{\"kind\":\"waitFor\",\"selector\":\".datamachine-flow-card--empty button\"},{\"kind\":\"click\",\"selector\":\".datamachine-flow-card--empty button\"},{\"kind\":\"waitFor\",\"waitFor\":\"duration\",\"duration\":\"500ms\"},{\"kind\":\"click\",\"selector\":\".datamachine-flow-card--empty button\"},{\"kind\":\"navigate\",\"url\":\"/wp-admin/admin.php?page=datamachine-pipelines\",\"waitFor\":\"networkidle\"},{\"kind\":\"expect\",\"selector\":\".datamachine-flows-list\",\"state\":\"visible\"},{\"kind\":\"assertObservation\",\"assertion\":\"no-console-errors\"},{\"kind\":\"assertObservation\",\"assertion\":\"no-page-errors\"}]", "capture=steps,console,errors,network,screenshot"] }],
"action": [{ "command": "wordpress.browser-actions", "args": ["auth=wordpress-admin", "step-timeout=30s", "steps-json=[{\"kind\":\"navigate\",\"url\":\"/wp-admin/admin.php?page=datamachine-pipelines\"},{\"kind\":\"waitFor\",\"selector\":\".datamachine-flow-card--empty button\"},{\"kind\":\"evaluate\",\"expression\":\"return (() => { window.__datamachineFlowCreates = []; const originalFetch = window.fetch.bind(window); window.fetch = async (input, init = {}) => { const response = await originalFetch(input, init); const url = typeof input === 'string' ? input : input.url; const method = init.method || (typeof input === 'object' ? input.method : 'GET'); if (url.includes('/datamachine/v1/flows') && method.toUpperCase() === 'POST') { window.__datamachineFlowCreates.push({ status: response.status, data: await response.clone().json() }); } return response; }; })();\"},{\"kind\":\"click\",\"selector\":\".datamachine-flow-card--empty button\"},{\"kind\":\"waitFor\",\"waitFor\":\"duration\",\"duration\":\"500ms\"},{\"kind\":\"click\",\"selector\":\".datamachine-flow-card--empty button\"},{\"kind\":\"waitFor\",\"waitFor\":\"duration\",\"duration\":\"500ms\"},{\"kind\":\"evaluate\",\"expression\":\"return (() => { const creates = window.__datamachineFlowCreates; if (creates.length !== 2) throw new Error('Expected two flow POST responses, received ' + creates.length); if (creates.some(({ status }) => status < 200 || status >= 300)) throw new Error('Flow POST returned non-2xx: ' + JSON.stringify(creates)); if (creates.some(({ data }) => data.code === 'flow_creation_failed')) throw new Error('Flow creation returned flow_creation_failed'); const ids = creates.map(({ data }) => data.data.flow_id); if (new Set(ids).size !== 2) throw new Error('Flow POSTs did not return distinct IDs: ' + JSON.stringify(ids)); return ids; })();\"},{\"kind\":\"navigate\",\"url\":\"/wp-admin/admin.php?page=datamachine-pipelines\",\"waitFor\":\"networkidle\"},{\"kind\":\"expect\",\"selector\":\".datamachine-flows-list\",\"state\":\"visible\"},{\"kind\":\"evaluate\",\"expression\":\"return (() => { const siblings = document.querySelectorAll('.datamachine-flows-list .datamachine-flow-card'); if (siblings.length !== 2) throw new Error('Expected two sibling flows after reload, found ' + siblings.length); return siblings.length; })();\"},{\"kind\":\"assertObservation\",\"assertion\":\"no-console-errors\"},{\"kind\":\"assertObservation\",\"assertion\":\"no-page-errors\"}]", "capture=steps,console,errors,network,screenshot"] }],
"teardown": [{ "command": "wp-codebox.checkpoint-restore", "args": ["name=flow-sibling-preservation"] }]
}
},
Expand Down
Loading