-- Copyright (c) Microsoft Corporation. -- Licensed under the PostgreSQL License. -- Tests: Cancel status consistency across df.status(), df.list_instances(), df.instance_info() -- -- Regression test for: "cancellation status mismatch between df.status() and df.list_instances()" -- Root cause: df.list_instances() / df.instance_info() were reading status from duroxide -- (which reports "Failed"), while df.status() reads from df.instances (which stores "cancelled"). -- -- After the fix all three APIs read status from df.instances, so they always agree. -- -- Scenarios covered: -- 1. Normal completion — all APIs report 'completed' -- 2. Failure — all APIs report 'failed' -- 3. Cancel running — all APIs report 'cancelled' -- 4. Cancel terminal — df.cancel on already-completed/failed instance is a no-op SET SESSION AUTHORIZATION df_e2e_user; -- Helper: assert that df.status(), df.list_instances(), and df.instance_info() all return -- the same status value for the given instance. CREATE OR REPLACE FUNCTION df_e2e_assert_status_consistent( p_instance_id TEXT, p_expected_status TEXT, p_scenario TEXT ) RETURNS VOID LANGUAGE plpgsql AS $$ DECLARE v_status TEXT; v_list_status TEXT; v_info_status TEXT; BEGIN SELECT s INTO v_status FROM df.status(p_instance_id) s; SELECT l.status INTO v_list_status FROM df.list_instances() l WHERE l.instance_id = p_instance_id; SELECT i.status INTO v_info_status FROM df.instance_info(p_instance_id) i; IF lower(v_status) != lower(p_expected_status) THEN RAISE EXCEPTION 'FAILED [%]: df.status() returned %, expected %', p_scenario, v_status, p_expected_status; END IF; IF lower(v_list_status) != lower(p_expected_status) THEN RAISE EXCEPTION 'FAILED [%]: df.list_instances() returned %, expected %', p_scenario, v_list_status, p_expected_status; END IF; IF lower(v_info_status) != lower(p_expected_status) THEN RAISE EXCEPTION 'FAILED [%]: df.instance_info() returned %, expected %', p_scenario, v_info_status, p_expected_status; END IF; RAISE NOTICE 'PASSED [%]: all three APIs agree on status = %', p_scenario, p_expected_status; END $$; -- =========================================================================== -- Scenario 1: Normal completion -- =========================================================================== CREATE TEMP TABLE _t_complete (instance_id TEXT); INSERT INTO _t_complete SELECT df.start('SELECT 42', 'cancel-consistency-complete'); DO $$ DECLARE inst_id TEXT; status TEXT; BEGIN SELECT instance_id INTO inst_id FROM _t_complete; SELECT df.await_instance(inst_id, 30) INTO status; IF lower(status) != 'completed' THEN RAISE EXCEPTION 'Scenario 1 setup failed: expected completed, got %', status; END IF; PERFORM df_e2e_assert_status_consistent(inst_id, 'completed', 'normal_completion'); END $$; DROP TABLE _t_complete; -- =========================================================================== -- Scenario 2: Failure mid-execution -- =========================================================================== CREATE TEMP TABLE _t_fail (instance_id TEXT); INSERT INTO _t_fail SELECT df.start( 'SELECT 1/0', -- division by zero --> forces failure 'cancel-consistency-fail' ); DO $$ DECLARE inst_id TEXT; status TEXT; attempts INT := 0; BEGIN SELECT instance_id INTO inst_id FROM _t_fail; -- Poll until failed LOOP SELECT s INTO status FROM df.status(inst_id) s; EXIT WHEN lower(status) = 'failed' OR attempts > 300; PERFORM pg_sleep(0.1); attempts := attempts + 1; END LOOP; IF lower(status) != 'failed' THEN RAISE EXCEPTION 'Scenario 2 setup failed: expected failed, got %', status; END IF; PERFORM df_e2e_assert_status_consistent(inst_id, 'failed', 'failure_mid_execution'); END $$; DROP TABLE _t_fail; -- =========================================================================== -- Scenario 3: Cancel of a running instance -- =========================================================================== DROP TABLE IF EXISTS _cancel_log; CREATE TABLE _cancel_log (id SERIAL); CREATE TEMP TABLE _t_cancel (instance_id TEXT); INSERT INTO _t_cancel SELECT df.start( df.loop( 'INSERT INTO _cancel_log DEFAULT VALUES' ~> df.sleep(1) ), 'cancel-consistency-running' ); DO $$ DECLARE inst_id TEXT; status TEXT; cnt INT; attempts INT := 0; BEGIN SELECT instance_id INTO inst_id FROM _t_cancel; -- Wait for at least 1 iteration so the loop is genuinely running LOOP SELECT COUNT(*) INTO cnt FROM _cancel_log; EXIT WHEN cnt >= 1 OR attempts > 200; PERFORM pg_sleep(0.1); attempts := attempts + 1; END LOOP; IF cnt < 1 THEN RAISE EXCEPTION 'Scenario 3 setup: loop never executed'; END IF; -- Cancel while running PERFORM df.cancel(inst_id, 'consistency-test'); -- df.cancel immediately sets df.instances.status = 'cancelled'. -- All three APIs should now agree. PERFORM df_e2e_assert_status_consistent(inst_id, 'cancelled', 'cancel_running_instance'); END $$; DROP TABLE _t_cancel; DROP TABLE _cancel_log; -- =========================================================================== -- Scenario 4: df.cancel on an already-terminal instance is a no-op -- =========================================================================== CREATE TEMP TABLE _t_noop (instance_id TEXT); INSERT INTO _t_noop SELECT df.start('SELECT 99', 'cancel-consistency-noop'); DO $$ DECLARE inst_id TEXT; status TEXT; BEGIN SELECT instance_id INTO inst_id FROM _t_noop; SELECT df.await_instance(inst_id, 30) INTO status; IF lower(status) != 'completed' THEN RAISE EXCEPTION 'Scenario 4 setup failed: expected completed, got %', status; END IF; -- Call df.cancel on an already-completed instance — must be a no-op PERFORM df.cancel(inst_id, 'should-be-ignored'); -- Status must remain 'completed' PERFORM df_e2e_assert_status_consistent(inst_id, 'completed', 'cancel_noop_on_completed'); END $$; DROP TABLE _t_noop; -- Cleanup helper function DROP FUNCTION IF EXISTS df_e2e_assert_status_consistent(TEXT, TEXT, TEXT); SELECT 'TEST PASSED: cancel status consistency' AS result;