@pgflow/core 0.0.5-prealpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +660 -0
- package/README.md +373 -0
- package/__tests__/mocks/index.ts +1 -0
- package/__tests__/mocks/postgres.ts +37 -0
- package/__tests__/types/PgflowSqlClient.test-d.ts +59 -0
- package/dist/LICENSE.md +660 -0
- package/dist/README.md +373 -0
- package/dist/index.js +54 -0
- package/docs/options_for_flow_and_steps.md +75 -0
- package/docs/pgflow-blob-reference-system.md +179 -0
- package/eslint.config.cjs +22 -0
- package/example-flow.mermaid +5 -0
- package/example-flow.svg +1 -0
- package/flow-lifecycle.mermaid +83 -0
- package/flow-lifecycle.svg +1 -0
- package/out-tsc/vitest/__tests__/mocks/index.d.ts +2 -0
- package/out-tsc/vitest/__tests__/mocks/index.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/mocks/postgres.d.ts +15 -0
- package/out-tsc/vitest/__tests__/mocks/postgres.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/PgflowSqlClient.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/PgflowSqlClient.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/tsconfig.spec.tsbuildinfo +1 -0
- package/out-tsc/vitest/vite.config.d.ts +3 -0
- package/out-tsc/vitest/vite.config.d.ts.map +1 -0
- package/package.json +28 -0
- package/pkgs/core/dist/index.js +54 -0
- package/pkgs/core/dist/pkgs/core/LICENSE.md +660 -0
- package/pkgs/core/dist/pkgs/core/README.md +373 -0
- package/pkgs/dsl/dist/index.js +123 -0
- package/pkgs/dsl/dist/pkgs/dsl/README.md +11 -0
- package/project.json +125 -0
- package/prompts/architect.md +87 -0
- package/prompts/condition.md +33 -0
- package/prompts/declarative_sql.md +15 -0
- package/prompts/deps_in_payloads.md +20 -0
- package/prompts/dsl-multi-arg.ts +48 -0
- package/prompts/dsl-options.md +39 -0
- package/prompts/dsl-single-arg.ts +51 -0
- package/prompts/dsl-two-arg.ts +61 -0
- package/prompts/dsl.md +119 -0
- package/prompts/fanout_steps.md +1 -0
- package/prompts/json_schemas.md +36 -0
- package/prompts/one_shot.md +286 -0
- package/prompts/pgtap.md +229 -0
- package/prompts/sdk.md +59 -0
- package/prompts/step_types.md +62 -0
- package/prompts/versioning.md +16 -0
- package/queries/fail_permanently.sql +17 -0
- package/queries/fail_task.sql +21 -0
- package/queries/sequential.sql +47 -0
- package/queries/two_roots_left_right.sql +59 -0
- package/schema.svg +1 -0
- package/scripts/colorize-pgtap-output.awk +72 -0
- package/scripts/run-test-with-colors +5 -0
- package/scripts/watch-test +7 -0
- package/src/PgflowSqlClient.ts +85 -0
- package/src/database-types.ts +759 -0
- package/src/index.ts +3 -0
- package/src/types.ts +103 -0
- package/supabase/config.toml +32 -0
- package/supabase/migrations/000000_schema.sql +150 -0
- package/supabase/migrations/000005_create_flow.sql +29 -0
- package/supabase/migrations/000010_add_step.sql +48 -0
- package/supabase/migrations/000015_start_ready_steps.sql +45 -0
- package/supabase/migrations/000020_start_flow.sql +46 -0
- package/supabase/migrations/000030_read_with_poll_backport.sql +70 -0
- package/supabase/migrations/000040_poll_for_tasks.sql +100 -0
- package/supabase/migrations/000045_maybe_complete_run.sql +30 -0
- package/supabase/migrations/000050_complete_task.sql +98 -0
- package/supabase/migrations/000055_calculate_retry_delay.sql +11 -0
- package/supabase/migrations/000060_fail_task.sql +124 -0
- package/supabase/migrations/000_edge_worker_initial.sql +86 -0
- package/supabase/seed.sql +202 -0
- package/supabase/tests/add_step/basic_step_addition.test.sql +29 -0
- package/supabase/tests/add_step/circular_dependency.test.sql +21 -0
- package/supabase/tests/add_step/flow_isolation.test.sql +26 -0
- package/supabase/tests/add_step/idempotent_step_addition.test.sql +20 -0
- package/supabase/tests/add_step/invalid_step_slug.test.sql +16 -0
- package/supabase/tests/add_step/nonexistent_dependency.test.sql +16 -0
- package/supabase/tests/add_step/nonexistent_flow.test.sql +13 -0
- package/supabase/tests/add_step/options.test.sql +66 -0
- package/supabase/tests/add_step/step_with_dependency.test.sql +36 -0
- package/supabase/tests/add_step/step_with_multiple_dependencies.test.sql +46 -0
- package/supabase/tests/complete_task/archives_message.test.sql +67 -0
- package/supabase/tests/complete_task/completes_run_if_no_more_remaining_steps.test.sql +62 -0
- package/supabase/tests/complete_task/completes_task_and_updates_dependents.test.sql +64 -0
- package/supabase/tests/complete_task/decrements_remaining_steps_if_completing_step.test.sql +62 -0
- package/supabase/tests/complete_task/saves_output_when_completing_run.test.sql +57 -0
- package/supabase/tests/create_flow/flow_creation.test.sql +27 -0
- package/supabase/tests/create_flow/idempotency_and_duplicates.test.sql +26 -0
- package/supabase/tests/create_flow/invalid_slug.test.sql +13 -0
- package/supabase/tests/create_flow/options.test.sql +57 -0
- package/supabase/tests/fail_task/exponential_backoff.test.sql +70 -0
- package/supabase/tests/fail_task/mark_as_failed_if_no_retries_available.test.sql +49 -0
- package/supabase/tests/fail_task/respects_flow_retry_settings.test.sql +48 -0
- package/supabase/tests/fail_task/respects_step_retry_settings.test.sql +48 -0
- package/supabase/tests/fail_task/retry_task_if_retries_available.test.sql +39 -0
- package/supabase/tests/is_valid_slug.test.sql +72 -0
- package/supabase/tests/poll_for_tasks/builds_proper_input_from_deps_outputs.test.sql +35 -0
- package/supabase/tests/poll_for_tasks/hides_messages.test.sql +35 -0
- package/supabase/tests/poll_for_tasks/increments_attempts_count.test.sql +35 -0
- package/supabase/tests/poll_for_tasks/multiple_task_processing.test.sql +24 -0
- package/supabase/tests/poll_for_tasks/polls_only_queued_tasks.test.sql +35 -0
- package/supabase/tests/poll_for_tasks/reads_messages.test.sql +38 -0
- package/supabase/tests/poll_for_tasks/returns_no_tasks_if_no_step_task_for_message.test.sql +34 -0
- package/supabase/tests/poll_for_tasks/returns_no_tasks_if_queue_is_empty.test.sql +19 -0
- package/supabase/tests/poll_for_tasks/returns_no_tasks_when_qty_set_to_0.test.sql +22 -0
- package/supabase/tests/poll_for_tasks/sets_vt_delay_based_on_opt_timeout.test.sql +41 -0
- package/supabase/tests/poll_for_tasks/tasks_reapppear_if_not_processed_in_time.test.sql +59 -0
- package/supabase/tests/start_flow/creates_run.test.sql +24 -0
- package/supabase/tests/start_flow/creates_step_states_for_all_steps.test.sql +25 -0
- package/supabase/tests/start_flow/creates_step_tasks_only_for_root_steps.test.sql +54 -0
- package/supabase/tests/start_flow/returns_run.test.sql +24 -0
- package/supabase/tests/start_flow/sends_messages_on_the_queue.test.sql +50 -0
- package/supabase/tests/start_flow/starts_only_root_steps.test.sql +21 -0
- package/supabase/tests/step_dsl_is_idempotent.test.sql +34 -0
- package/tsconfig.json +16 -0
- package/tsconfig.lib.json +26 -0
- package/tsconfig.spec.json +35 -0
- package/vite.config.ts +57 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(6);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- SETUP: Create flow with steps
|
|
6
|
+
select pgflow.create_flow('test_flow');
|
|
7
|
+
select pgflow.add_step('test_flow', 'default_options');
|
|
8
|
+
select pgflow.add_step('test_flow', 'overriden_max_attempts', max_attempts => 20);
|
|
9
|
+
select pgflow.add_step('test_flow', 'overriden_base_delay', base_delay => 30);
|
|
10
|
+
select pgflow.add_step('test_flow', 'overriden_timeout', timeout => 30);
|
|
11
|
+
|
|
12
|
+
-- TEST: opt_max_attempts, opt_base_delay and opt_timeout are NULL by default
|
|
13
|
+
select results_eq(
|
|
14
|
+
$$ SELECT opt_max_attempts is null, opt_base_delay is null, opt_timeout is null
|
|
15
|
+
FROM pgflow.steps
|
|
16
|
+
WHERE step_slug = 'default_options' $$,
|
|
17
|
+
$$ VALUES (true, true, true) $$,
|
|
18
|
+
'opt_max_attempts, opt_base_delay and opt_timeout are NULL by default'
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- TEST: opt_max_attempts can be set
|
|
22
|
+
select is(
|
|
23
|
+
(select opt_max_attempts from pgflow.steps where step_slug = 'overriden_max_attempts'),
|
|
24
|
+
20,
|
|
25
|
+
'opt_max_attempts can be set'
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
-- TEST: opt_base_delay can be set
|
|
29
|
+
select is(
|
|
30
|
+
(select opt_base_delay from pgflow.steps where step_slug = 'overriden_base_delay'),
|
|
31
|
+
30,
|
|
32
|
+
'opt_base_delay can be set'
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- TEST: opt_timeout can be set
|
|
36
|
+
select is(
|
|
37
|
+
(select opt_timeout from pgflow.steps where step_slug = 'overriden_timeout'),
|
|
38
|
+
30,
|
|
39
|
+
'opt_timeout can be set'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
-- SETUP: Add same step again to make sure it doesnt get updated
|
|
43
|
+
select pgflow.add_step('test_flow', 'added_twice', max_attempts => 10, base_delay => 15, timeout => 90);
|
|
44
|
+
select pgflow.add_step('test_flow', 'added_twice', max_attempts => 20, base_delay => 30, timeout => 30);
|
|
45
|
+
|
|
46
|
+
--TEST: Should not update opt_max_attempts, opt_base_delay and opt_timeout
|
|
47
|
+
select results_eq(
|
|
48
|
+
$$ SELECT opt_max_attempts, opt_base_delay, opt_timeout FROM pgflow.steps WHERE step_slug = 'added_twice' $$,
|
|
49
|
+
$$ VALUES (10, 15, 90) $$,
|
|
50
|
+
'Should not update opt_max_attempts, opt_base_delay and opt_timeout'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
-- SETUP: Add step defined with default values again to make sure it doesnt get updated
|
|
54
|
+
select pgflow.add_step('test_flow', 'default_options', max_attempts => 0, base_delay => 15, timeout => 90);
|
|
55
|
+
|
|
56
|
+
-- TEST: Should not update opt_max_attempts, opt_base_delay and opt_timeout
|
|
57
|
+
select results_eq(
|
|
58
|
+
$$ SELECT opt_max_attempts is null, opt_base_delay is null, opt_timeout is null
|
|
59
|
+
FROM pgflow.steps
|
|
60
|
+
WHERE step_slug = 'default_options' $$,
|
|
61
|
+
$$ VALUES (true, true, true) $$,
|
|
62
|
+
'Should not update opt_max_attempts, opt_base_delay and opt_timeout for step with default values'
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
select * from finish();
|
|
66
|
+
rollback;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(3);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- Setup
|
|
6
|
+
select pgflow.create_flow('test_flow');
|
|
7
|
+
select pgflow.add_step('test_flow', 'first_step');
|
|
8
|
+
|
|
9
|
+
-- Test
|
|
10
|
+
select pgflow.add_step('test_flow', 'second_step', array['first_step']);
|
|
11
|
+
select results_eq(
|
|
12
|
+
$$ SELECT step_slug FROM pgflow.steps WHERE flow_slug = 'test_flow' ORDER BY step_slug $$,
|
|
13
|
+
array['first_step', 'second_step']::text [],
|
|
14
|
+
'Second step should be added to the steps table'
|
|
15
|
+
);
|
|
16
|
+
select is(
|
|
17
|
+
(
|
|
18
|
+
select deps_count::int
|
|
19
|
+
from pgflow.steps
|
|
20
|
+
where flow_slug = 'test_flow' and step_slug = 'second_step'
|
|
21
|
+
),
|
|
22
|
+
1::int,
|
|
23
|
+
'deps_count should be 1 because "second_step" has one dependency'
|
|
24
|
+
);
|
|
25
|
+
select results_eq(
|
|
26
|
+
$$
|
|
27
|
+
SELECT dep_slug, step_slug
|
|
28
|
+
FROM pgflow.deps WHERE flow_slug = 'test_flow'
|
|
29
|
+
ORDER BY dep_slug, step_slug
|
|
30
|
+
$$,
|
|
31
|
+
$$ VALUES ('first_step', 'second_step') $$,
|
|
32
|
+
'Dependency should be recorded in deps table'
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
select * from finish();
|
|
36
|
+
rollback;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(3);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- Setup
|
|
6
|
+
select pgflow.create_flow('test_flow');
|
|
7
|
+
select pgflow.add_step('test_flow', 'first_step');
|
|
8
|
+
select pgflow.add_step('test_flow', 'second_step', array['first_step']);
|
|
9
|
+
|
|
10
|
+
-- Test
|
|
11
|
+
select pgflow.add_step('test_flow', 'third_step', array['second_step']);
|
|
12
|
+
select
|
|
13
|
+
pgflow.add_step('test_flow', 'fourth_step', array['second_step', 'third_step']);
|
|
14
|
+
select results_eq(
|
|
15
|
+
$$
|
|
16
|
+
SELECT step_slug FROM pgflow.steps WHERE flow_slug = 'test_flow' ORDER BY step_slug
|
|
17
|
+
$$,
|
|
18
|
+
array['first_step', 'fourth_step', 'second_step', 'third_step']::text [],
|
|
19
|
+
'All steps should be in the steps table'
|
|
20
|
+
);
|
|
21
|
+
select is(
|
|
22
|
+
(
|
|
23
|
+
select deps_count::int
|
|
24
|
+
from pgflow.steps
|
|
25
|
+
where flow_slug = 'test_flow' and step_slug = 'fourth_step'
|
|
26
|
+
),
|
|
27
|
+
2::int,
|
|
28
|
+
'deps_count should be 2 because "fourth_step" have two dependencies'
|
|
29
|
+
);
|
|
30
|
+
select set_eq(
|
|
31
|
+
$$
|
|
32
|
+
SELECT dep_slug, step_slug
|
|
33
|
+
FROM pgflow.deps
|
|
34
|
+
WHERE flow_slug = 'test_flow'
|
|
35
|
+
$$,
|
|
36
|
+
$$ VALUES
|
|
37
|
+
('first_step', 'second_step'),
|
|
38
|
+
('second_step', 'third_step'),
|
|
39
|
+
('second_step', 'fourth_step'),
|
|
40
|
+
('third_step', 'fourth_step')
|
|
41
|
+
$$,
|
|
42
|
+
'All dependencies should be correctly recorded'
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
select * from finish();
|
|
46
|
+
rollback;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(5);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
select pgflow_tests.setup_flow('sequential');
|
|
5
|
+
|
|
6
|
+
-- SETUP
|
|
7
|
+
select pgflow.start_flow('sequential', '{"test": true}'::JSONB);
|
|
8
|
+
|
|
9
|
+
-- TEST: First message shoud be in the queue
|
|
10
|
+
select is(
|
|
11
|
+
(select message ->> 'step_slug' from pgmq.q_sequential limit 1),
|
|
12
|
+
'first',
|
|
13
|
+
'First message should be in the queue'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- SETUP
|
|
17
|
+
select pgflow.complete_task(
|
|
18
|
+
(select run_id from pgflow.runs limit 1),
|
|
19
|
+
'first',
|
|
20
|
+
0,
|
|
21
|
+
'"first was successful"'::JSONB
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
-- TEST: First message shoud be archived
|
|
25
|
+
select is(
|
|
26
|
+
(
|
|
27
|
+
select count(*)::INT
|
|
28
|
+
from pgmq.q_sequential
|
|
29
|
+
where message ->> 'step_slug' = 'first'
|
|
30
|
+
),
|
|
31
|
+
0::INT,
|
|
32
|
+
'There should be no messages in the queue'
|
|
33
|
+
);
|
|
34
|
+
select is(
|
|
35
|
+
(
|
|
36
|
+
select count(*)::INT
|
|
37
|
+
from pgmq.a_sequential
|
|
38
|
+
where message ->> 'step_slug' = 'first'
|
|
39
|
+
limit 1
|
|
40
|
+
),
|
|
41
|
+
1::INT,
|
|
42
|
+
'The message should be archived'
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
-- TEST: Other messages shoud not be archived
|
|
46
|
+
select is(
|
|
47
|
+
(
|
|
48
|
+
select count(*)::INT
|
|
49
|
+
from pgmq.q_sequential
|
|
50
|
+
where message ->> 'step_slug' = 'second'
|
|
51
|
+
),
|
|
52
|
+
1::INT,
|
|
53
|
+
'There should be no messages in the queue'
|
|
54
|
+
);
|
|
55
|
+
select is(
|
|
56
|
+
(
|
|
57
|
+
select count(*)::INT
|
|
58
|
+
from pgmq.a_sequential
|
|
59
|
+
where message ->> 'step_slug' = 'second'
|
|
60
|
+
limit 1
|
|
61
|
+
),
|
|
62
|
+
0::INT,
|
|
63
|
+
'The other message should not be archived'
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
select finish();
|
|
67
|
+
rollback;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(4);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
select pgflow_tests.setup_flow('sequential');
|
|
5
|
+
|
|
6
|
+
-- Start the flow
|
|
7
|
+
select pgflow.start_flow('sequential', '{"test": true}'::JSONB);
|
|
8
|
+
|
|
9
|
+
-- TEST: Initial remaining_steps should be 3 and status should be 'started'
|
|
10
|
+
select results_eq(
|
|
11
|
+
$$ SELECT remaining_steps::int, status FROM pgflow.runs LIMIT 1 $$,
|
|
12
|
+
$$ VALUES (3::int, 'started'::text) $$,
|
|
13
|
+
'Initial remaining_steps should be 3 and status should be started'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- Complete the first step's task
|
|
17
|
+
select pgflow.complete_task(
|
|
18
|
+
(select run_id from pgflow.runs limit 1),
|
|
19
|
+
'first',
|
|
20
|
+
0,
|
|
21
|
+
'"first was successful"'::JSONB
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
-- TEST: After completing first step, remaining_steps should be 2 and status still 'started'
|
|
25
|
+
select results_eq(
|
|
26
|
+
$$ SELECT remaining_steps::int, status FROM pgflow.runs LIMIT 1 $$,
|
|
27
|
+
$$ VALUES (2::int, 'started'::text) $$,
|
|
28
|
+
'After completing first step, remaining_steps should be 2 and status still started'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
-- Complete the second step's task
|
|
32
|
+
select pgflow.complete_task(
|
|
33
|
+
(select run_id from pgflow.runs limit 1),
|
|
34
|
+
'second',
|
|
35
|
+
0,
|
|
36
|
+
'"second was successful"'::JSONB
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
-- TEST: After completing second step, remaining_steps should be 1 and status still 'started'
|
|
40
|
+
select results_eq(
|
|
41
|
+
$$ SELECT remaining_steps::int, status FROM pgflow.runs LIMIT 1 $$,
|
|
42
|
+
$$ VALUES (1::int, 'started'::text) $$,
|
|
43
|
+
'After completing second step, remaining_steps should be 1 and status still started'
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
-- Complete the last step's task
|
|
47
|
+
select pgflow.complete_task(
|
|
48
|
+
(select run_id from pgflow.runs limit 1),
|
|
49
|
+
'last',
|
|
50
|
+
0,
|
|
51
|
+
'"last was successful"'::JSONB
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
-- TEST: Final remaining_steps should be 0 and status should be 'completed'
|
|
55
|
+
select results_eq(
|
|
56
|
+
$$ SELECT remaining_steps::int, status FROM pgflow.runs LIMIT 1 $$,
|
|
57
|
+
$$ VALUES (0::int, 'completed'::text) $$,
|
|
58
|
+
'Final remaining_steps should be 0 and status should be completed'
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
select finish();
|
|
62
|
+
rollback;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(5);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
select pgflow_tests.setup_flow('sequential');
|
|
5
|
+
|
|
6
|
+
-- Start a flow run
|
|
7
|
+
select pgflow.start_flow('sequential', '"hello"'::jsonb);
|
|
8
|
+
|
|
9
|
+
-- Complete the first task
|
|
10
|
+
select pgflow.complete_task(
|
|
11
|
+
(select run_id from pgflow.runs limit 1),
|
|
12
|
+
'first',
|
|
13
|
+
0,
|
|
14
|
+
'{"result": "first completed"}'::jsonb
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- TEST: Task should be marked as completed with correct output
|
|
18
|
+
select results_eq(
|
|
19
|
+
$$ SELECT status, output FROM pgflow.step_tasks
|
|
20
|
+
WHERE run_id = (SELECT run_id FROM pgflow.runs LIMIT 1)
|
|
21
|
+
AND step_slug = 'first' AND task_index = 0 $$,
|
|
22
|
+
$$ VALUES ('completed', '{"result": "first completed"}'::jsonb) $$,
|
|
23
|
+
'Task should be marked as completed with correct output'
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- TEST: Step state should be marked as completed
|
|
27
|
+
select results_eq(
|
|
28
|
+
$$ SELECT status, remaining_tasks FROM pgflow.step_states
|
|
29
|
+
WHERE run_id = (SELECT run_id FROM pgflow.runs LIMIT 1)
|
|
30
|
+
AND step_slug = 'first' $$,
|
|
31
|
+
$$ VALUES ('completed', 0) $$,
|
|
32
|
+
'Step state should be marked as completed with no remaining tasks'
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- TEST: Dependent step should have remaining_deps decremented
|
|
36
|
+
select results_eq(
|
|
37
|
+
$$ SELECT remaining_deps FROM pgflow.step_states
|
|
38
|
+
WHERE run_id = (SELECT run_id FROM pgflow.runs LIMIT 1)
|
|
39
|
+
AND step_slug = 'second' $$,
|
|
40
|
+
$$ VALUES (0) $$,
|
|
41
|
+
'Dependent step should have remaining_deps decremented to 0'
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
-- TEST: Dependent step task should be created and queued
|
|
45
|
+
select results_eq(
|
|
46
|
+
$$ SELECT status FROM pgflow.step_tasks
|
|
47
|
+
WHERE run_id = (SELECT run_id FROM pgflow.runs LIMIT 1)
|
|
48
|
+
AND step_slug = 'second' $$,
|
|
49
|
+
$$ VALUES ('queued') $$,
|
|
50
|
+
'Dependent step task should be created and queued'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
-- TEST: Message should be in the queue for the dependent step
|
|
54
|
+
select is(
|
|
55
|
+
(
|
|
56
|
+
select count(*)::int from pgmq.q_sequential
|
|
57
|
+
where message ->> 'step_slug' = 'second'
|
|
58
|
+
),
|
|
59
|
+
1::int,
|
|
60
|
+
'Message should be in the queue for the dependent step'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
select finish();
|
|
64
|
+
rollback;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(4);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
select pgflow_tests.setup_flow('sequential');
|
|
5
|
+
|
|
6
|
+
-- Start the flow
|
|
7
|
+
select pgflow.start_flow('sequential', '{"test": true}'::JSONB);
|
|
8
|
+
|
|
9
|
+
-- TEST: Initial remaining_steps should be 3
|
|
10
|
+
select is(
|
|
11
|
+
(select remaining_steps::INT from pgflow.runs limit 1),
|
|
12
|
+
3::INT,
|
|
13
|
+
'Initial remaining_steps should be 3'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- Complete the first step's task
|
|
17
|
+
select pgflow.complete_task(
|
|
18
|
+
(select run_id from pgflow.runs limit 1),
|
|
19
|
+
'first',
|
|
20
|
+
0,
|
|
21
|
+
'{"result": "success"}'::JSONB
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
-- TEST: After completing first step, remaining_steps should be 2
|
|
25
|
+
select is(
|
|
26
|
+
(select remaining_steps::INT from pgflow.runs limit 1),
|
|
27
|
+
2::INT,
|
|
28
|
+
'After completing first step, remaining_steps should be 2'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
-- Complete the second step's task
|
|
32
|
+
select pgflow.complete_task(
|
|
33
|
+
(select run_id from pgflow.runs limit 1),
|
|
34
|
+
'second',
|
|
35
|
+
0,
|
|
36
|
+
'{"result": "success"}'::JSONB
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
-- TEST: After completing second step, remaining_steps should be 1
|
|
40
|
+
select is(
|
|
41
|
+
(select remaining_steps::INT from pgflow.runs limit 1),
|
|
42
|
+
1::INT,
|
|
43
|
+
'After completing second step, remaining_steps should be 1'
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
-- Complete the last step's task
|
|
47
|
+
select pgflow.complete_task(
|
|
48
|
+
(select run_id from pgflow.runs limit 1),
|
|
49
|
+
'last',
|
|
50
|
+
0,
|
|
51
|
+
'{"result": "success"}'::JSONB
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
-- TEST: Final remaining_steps should be 0
|
|
55
|
+
select is(
|
|
56
|
+
(select remaining_steps::INT from pgflow.runs limit 1),
|
|
57
|
+
0::INT,
|
|
58
|
+
'Final remaining_steps should be 0'
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
select finish();
|
|
62
|
+
rollback;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(2);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
select pgflow_tests.setup_flow('two_roots_left_right');
|
|
5
|
+
|
|
6
|
+
-- Start the flow
|
|
7
|
+
select pgflow.start_flow('two_roots_left_right', '"hello"'::JSONB);
|
|
8
|
+
|
|
9
|
+
-- Complete all the steps
|
|
10
|
+
select pgflow.complete_task(
|
|
11
|
+
(select run_id from pgflow.runs limit 1),
|
|
12
|
+
'connected_root',
|
|
13
|
+
0,
|
|
14
|
+
'"root successful"'::JSONB
|
|
15
|
+
);
|
|
16
|
+
select pgflow.complete_task(
|
|
17
|
+
(select run_id from pgflow.runs limit 1),
|
|
18
|
+
'left',
|
|
19
|
+
0,
|
|
20
|
+
'"left successful"'::JSONB
|
|
21
|
+
);
|
|
22
|
+
select pgflow.complete_task(
|
|
23
|
+
(select run_id from pgflow.runs limit 1),
|
|
24
|
+
'right',
|
|
25
|
+
0,
|
|
26
|
+
'"right successful"'::JSONB
|
|
27
|
+
);
|
|
28
|
+
select pgflow.complete_task(
|
|
29
|
+
(select run_id from pgflow.runs limit 1),
|
|
30
|
+
'disconnected_root',
|
|
31
|
+
0,
|
|
32
|
+
'"disconnected successful"'::JSONB
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- TEST: Make sure that run is completed
|
|
36
|
+
select results_eq(
|
|
37
|
+
$$ SELECT status::text, remaining_steps::int FROM pgflow.runs LIMIT 1 $$,
|
|
38
|
+
$$ VALUES ('completed'::text, 0::int) $$,
|
|
39
|
+
'Run was completed'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
-- noqa: disable=all
|
|
43
|
+
PREPARE expected_output AS SELECT
|
|
44
|
+
jsonb_build_object(
|
|
45
|
+
'disconnected_root', '"disconnected successful"'::JSONB,
|
|
46
|
+
'left', '"left successful"'::JSONB,
|
|
47
|
+
'right', '"right successful"'::JSONB
|
|
48
|
+
);
|
|
49
|
+
-- noqa: enable=all
|
|
50
|
+
SELECT results_eq (
|
|
51
|
+
$$ SELECT output FROM pgflow.runs LIMIT 1 $$,
|
|
52
|
+
'expected_output',
|
|
53
|
+
'Outputs of all final steps were saved as run output'
|
|
54
|
+
) ;
|
|
55
|
+
|
|
56
|
+
SELECT finish () ;
|
|
57
|
+
ROLLBACK ;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(2);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- Clean up any existing test queue
|
|
6
|
+
select pgmq.drop_queue('test_flow')
|
|
7
|
+
from pgmq.list_queues()
|
|
8
|
+
where queue_name = 'test_flow'
|
|
9
|
+
limit 1;
|
|
10
|
+
|
|
11
|
+
-- TEST: Flow should be added to the flows table
|
|
12
|
+
select pgflow.create_flow('test_flow');
|
|
13
|
+
select results_eq(
|
|
14
|
+
$$ SELECT flow_slug FROM pgflow.flows $$,
|
|
15
|
+
array['test_flow']::text [],
|
|
16
|
+
'Flow should be added to the flows table'
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
-- TEST: Creating a flow should create a PGMQ queue with the same name
|
|
20
|
+
select results_eq(
|
|
21
|
+
$$ SELECT EXISTS(SELECT 1 FROM pgmq.list_queues() WHERE queue_name = 'test_flow') $$,
|
|
22
|
+
array[true],
|
|
23
|
+
'Creating a flow should create a PGMQ queue with the same name'
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
select * from finish();
|
|
27
|
+
rollback;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(2);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- Setup initial flow
|
|
6
|
+
select pgflow.create_flow('test_flow');
|
|
7
|
+
|
|
8
|
+
-- SETUP: Create flow again to ensure it doesn't throw
|
|
9
|
+
select pgflow.create_flow('test_flow');
|
|
10
|
+
|
|
11
|
+
-- TEST: No duplicate flow should be created
|
|
12
|
+
select results_eq(
|
|
13
|
+
$$ SELECT flow_slug FROM pgflow.flows $$,
|
|
14
|
+
array['test_flow']::text [],
|
|
15
|
+
'No duplicate flow should be created'
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
--TEST: Creating a flow with existing flow_slug should still return the flow
|
|
19
|
+
select results_eq(
|
|
20
|
+
$$ SELECT flow_slug FROM pgflow.create_flow('test_flow') $$,
|
|
21
|
+
$$ VALUES ('test_flow') $$,
|
|
22
|
+
'Creating a flow with existing flow_slug should still return the flow'
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
select * from finish();
|
|
26
|
+
rollback;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(1);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- TEST: Should detect and prevent invalid flow slug
|
|
6
|
+
select throws_ok(
|
|
7
|
+
$$ SELECT pgflow.create_flow('invalid-flow') $$,
|
|
8
|
+
'new row for relation "flows" violates check constraint "slug_is_valid"',
|
|
9
|
+
'Should detect and prevent invalid flow slug'
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
select * from finish();
|
|
13
|
+
rollback;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(5);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- SETUP: flow with all default values
|
|
6
|
+
select pgflow.create_flow('test_flow');
|
|
7
|
+
|
|
8
|
+
--TEST: Should create flow with default max_attempts, base_delay and timeout
|
|
9
|
+
select results_eq(
|
|
10
|
+
$$ SELECT opt_max_attempts, opt_base_delay, opt_timeout FROM pgflow.create_flow('test_flow') $$,
|
|
11
|
+
$$ VALUES (3, 5, 60) $$,
|
|
12
|
+
'Should create flow with default opt_max_attempts, opt_base_delay and opt_timeout'
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
-- SETUP: flow with overriden max_attempts
|
|
16
|
+
select pgflow.create_flow('test_flow_2', max_attempts => 10);
|
|
17
|
+
|
|
18
|
+
--TEST: Should allow overriding opt_max_attempts
|
|
19
|
+
select results_eq(
|
|
20
|
+
$$ SELECT opt_max_attempts, opt_base_delay, opt_timeout FROM pgflow.create_flow('test_flow_2') $$,
|
|
21
|
+
$$ VALUES (10, 5, 60) $$,
|
|
22
|
+
'Should allow overriding opt_max_attempts'
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
-- SETUP: flow with overriden opt_base_delay
|
|
26
|
+
select pgflow.create_flow('test_flow_3', base_delay => 10);
|
|
27
|
+
|
|
28
|
+
--TEST: Should allow overriding base_delay
|
|
29
|
+
select results_eq(
|
|
30
|
+
$$ SELECT opt_max_attempts, opt_base_delay, opt_timeout FROM pgflow.create_flow('test_flow_3') $$,
|
|
31
|
+
$$ VALUES (3, 10, 60) $$,
|
|
32
|
+
'Should allow overriding opt_base_delay'
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- SETUP: flow with overriden opt_timeout
|
|
36
|
+
select pgflow.create_flow('test_flow_5', timeout => 7200);
|
|
37
|
+
|
|
38
|
+
--TEST: Should allow overriding timeout
|
|
39
|
+
select results_eq(
|
|
40
|
+
$$ SELECT opt_max_attempts, opt_base_delay, opt_timeout FROM pgflow.create_flow('test_flow_5') $$,
|
|
41
|
+
$$ VALUES (3, 5, 7200) $$,
|
|
42
|
+
'Should allow overriding opt_timeout'
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
-- SETUP: create same flow again to make sure it doesnt get updated
|
|
46
|
+
select pgflow.create_flow('test_flow_4', max_attempts => 10, base_delay => 15, timeout => 30);
|
|
47
|
+
select pgflow.create_flow('test_flow_4', max_attempts => 20, base_delay => 30, timeout => 60);
|
|
48
|
+
|
|
49
|
+
--TEST: Should not update opt_max_attempts, opt_base_delay and opt_timeout
|
|
50
|
+
select results_eq(
|
|
51
|
+
$$ SELECT opt_max_attempts, opt_base_delay, opt_timeout FROM pgflow.create_flow('test_flow_4') $$,
|
|
52
|
+
$$ VALUES (10, 15, 30) $$,
|
|
53
|
+
'Should not update opt_max_attempts, opt_base_delay and opt_timeout'
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
select * from finish();
|
|
57
|
+
rollback;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(4);
|
|
3
|
+
select pgflow_tests.reset_db();
|
|
4
|
+
|
|
5
|
+
-- create a test flow with two steps that have different base delays
|
|
6
|
+
select pgflow.create_flow('backoff_test');
|
|
7
|
+
select pgflow.add_step('backoff_test', 'first', max_attempts => 3, base_delay => 1);
|
|
8
|
+
select pgflow.add_step('backoff_test', 'last', max_attempts => 4, base_delay => 2);
|
|
9
|
+
|
|
10
|
+
-- start the flow with test data
|
|
11
|
+
select pgflow.start_flow('backoff_test', '{"test": true}'::jsonb);
|
|
12
|
+
|
|
13
|
+
-- simulate a task failure
|
|
14
|
+
select pgflow_tests.poll_and_fail('backoff_test', 1, 1);
|
|
15
|
+
|
|
16
|
+
-- make the message immediately visible (bypassing the retry delay)
|
|
17
|
+
select pgflow_tests.reset_message_visibility('backoff_test');
|
|
18
|
+
|
|
19
|
+
-- simulate a task failure
|
|
20
|
+
select pgflow_tests.poll_and_fail('backoff_test', 1, 1);
|
|
21
|
+
|
|
22
|
+
-- TEST: make sure we have proper attempts_count
|
|
23
|
+
select is(
|
|
24
|
+
(select attempts_count::int from pgflow.step_tasks where step_slug = 'first'),
|
|
25
|
+
2::int,
|
|
26
|
+
'first task should have 2 attempts'
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
-- TEST: verify exponential backoff is set properly
|
|
30
|
+
select is(
|
|
31
|
+
(select vt_seconds from pgflow_tests.message_timing('first', 'backoff_test') limit 1),
|
|
32
|
+
pgflow.calculate_retry_delay(1, 2),
|
|
33
|
+
'first step task should have visible time set to at least the base delay'
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
-- SETUP: proceed to next step
|
|
37
|
+
select pgflow_tests.reset_message_visibility('backoff_test');
|
|
38
|
+
select pgflow_tests.poll_and_complete('backoff_test', 1, 1);
|
|
39
|
+
select pgflow_tests.reset_message_visibility('backoff_test');
|
|
40
|
+
|
|
41
|
+
-- SETUP: fail twice to verify appropriate backoff
|
|
42
|
+
select pgflow_tests.poll_and_fail('backoff_test', 1, 1);
|
|
43
|
+
select pgflow_tests.reset_message_visibility('backoff_test');
|
|
44
|
+
select pgflow_tests.poll_and_fail('backoff_test', 1, 1);
|
|
45
|
+
select pgflow_tests.reset_message_visibility('backoff_test');
|
|
46
|
+
select pgflow_tests.poll_and_fail('backoff_test', 1, 1);
|
|
47
|
+
--
|
|
48
|
+
-- TEST: make sure we have proper attempts_count
|
|
49
|
+
select is(
|
|
50
|
+
(select attempts_count from pgflow.step_tasks where step_slug = 'last'),
|
|
51
|
+
3,
|
|
52
|
+
'last task should have 3 attempts'
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
-- TEST: verify exponential backoff is set properly
|
|
56
|
+
select pgflow_tests.assert_retry_delay(
|
|
57
|
+
queue_name => 'backoff_test',
|
|
58
|
+
step_slug => 'last',
|
|
59
|
+
expected_delay => pgflow.calculate_retry_delay(2, 3),
|
|
60
|
+
description => 'last step task should have visible time set to at least the base delay'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
-- select is(
|
|
64
|
+
-- (select vt_seconds from pgflow_tests.message_timing('last', 'backoff_test') limit 1),
|
|
65
|
+
-- pgflow.calculate_retry_delay(2, 3),
|
|
66
|
+
-- 'last step task should have visible time set to at least the base delay'
|
|
67
|
+
-- );
|
|
68
|
+
|
|
69
|
+
select finish();
|
|
70
|
+
rollback;
|