dbos 0.23.0a5__tar.gz → 0.23.0a8__tar.gz

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.

Potentially problematic release.


This version of dbos might be problematic. Click here for more details.

Files changed (92) hide show
  1. {dbos-0.23.0a5 → dbos-0.23.0a8}/PKG-INFO +6 -3
  2. {dbos-0.23.0a5 → dbos-0.23.0a8}/README.md +5 -2
  3. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_croniter.py +2 -2
  4. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +1 -1
  5. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_sys_db.py +15 -32
  6. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +1 -1
  7. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_workflow_commands.py +4 -0
  8. {dbos-0.23.0a5 → dbos-0.23.0a8}/pyproject.toml +1 -1
  9. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_croniter.py +2 -2
  10. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_workflow_cmds.py +15 -0
  11. {dbos-0.23.0a5 → dbos-0.23.0a8}/LICENSE +0 -0
  12. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/__init__.py +0 -0
  13. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_admin_server.py +0 -0
  14. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_app_db.py +0 -0
  15. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_classproperty.py +0 -0
  16. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_cloudutils/authentication.py +0 -0
  17. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_cloudutils/cloudutils.py +0 -0
  18. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_cloudutils/databases.py +0 -0
  19. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_context.py +0 -0
  20. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_core.py +0 -0
  21. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_db_wizard.py +0 -0
  22. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_dbos.py +0 -0
  23. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_dbos_config.py +0 -0
  24. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_error.py +0 -0
  25. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_fastapi.py +0 -0
  26. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_flask.py +0 -0
  27. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_kafka.py +0 -0
  28. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_kafka_message.py +0 -0
  29. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_logger.py +0 -0
  30. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/env.py +0 -0
  31. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/script.py.mako +0 -0
  32. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  33. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  34. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  35. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  36. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  37. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  38. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_outcome.py +0 -0
  39. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_queue.py +0 -0
  40. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_recovery.py +0 -0
  41. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_registrations.py +0 -0
  42. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_request.py +0 -0
  43. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_roles.py +0 -0
  44. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_scheduler.py +0 -0
  45. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_schemas/__init__.py +0 -0
  46. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_schemas/application_database.py +0 -0
  47. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_schemas/system_database.py +0 -0
  48. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_serialization.py +0 -0
  49. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/README.md +0 -0
  50. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  51. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
  52. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  53. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  54. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  55. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  56. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  57. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  58. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_tracer.py +0 -0
  59. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/_utils.py +0 -0
  60. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/cli/_github_init.py +0 -0
  61. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/cli/_template_init.py +0 -0
  62. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/cli/cli.py +0 -0
  63. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/dbos-config.schema.json +0 -0
  64. {dbos-0.23.0a5 → dbos-0.23.0a8}/dbos/py.typed +0 -0
  65. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/__init__.py +0 -0
  66. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/atexit_no_ctor.py +0 -0
  67. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/atexit_no_launch.py +0 -0
  68. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/classdefs.py +0 -0
  69. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/conftest.py +0 -0
  70. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/more_classdefs.py +0 -0
  71. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/queuedworkflow.py +0 -0
  72. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_admin_server.py +0 -0
  73. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_async.py +0 -0
  74. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_classdecorators.py +0 -0
  75. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_concurrency.py +0 -0
  76. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_config.py +0 -0
  77. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_dbos.py +0 -0
  78. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_failures.py +0 -0
  79. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_fastapi.py +0 -0
  80. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_fastapi_roles.py +0 -0
  81. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_flask.py +0 -0
  82. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_kafka.py +0 -0
  83. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_outcome.py +0 -0
  84. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_package.py +0 -0
  85. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_queue.py +0 -0
  86. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_scheduler.py +0 -0
  87. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_schema_migration.py +0 -0
  88. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_singleton.py +0 -0
  89. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_spans.py +0 -0
  90. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_sqlalchemy.py +0 -0
  91. {dbos-0.23.0a5 → dbos-0.23.0a8}/tests/test_workflow_cancel.py +0 -0
  92. {dbos-0.23.0a5 → dbos-0.23.0a8}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.23.0a5
3
+ Version: 0.23.0a8
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -78,6 +78,9 @@ You can use DBOS to add reliable background jobs or cron scheduling or queues to
78
78
  Install and configure with:
79
79
 
80
80
  ```shell
81
+ python3 -m venv dbos-example/.venv
82
+ cd dbos-example
83
+ source .venv/bin/activate
81
84
  pip install dbos
82
85
  dbos init --config
83
86
  ```
@@ -103,7 +106,7 @@ def step_two():
103
106
  def dbos_workflow():
104
107
  step_one()
105
108
  for _ in range(5):
106
- print("Press Control + \ to stop the app...")
109
+ print("Press Control + C twice to stop the app...")
107
110
  DBOS.sleep(1)
108
111
  step_two()
109
112
 
@@ -114,7 +117,7 @@ def fastapi_endpoint():
114
117
 
115
118
  Save the program into `main.py` and start it with `fastapi run`.
116
119
  Visit `localhost:8000` in your browser to start the workflow.
117
- When prompted, press `Control + \` to force quit your application.
120
+ When prompted, press `Control + C` (You may need to press `Control + C` twice quickly, or press `Control + \`, if `Control + C` is not effective in your environment) to force quit your application.
118
121
  It should crash midway through the workflow, having completed step one but not step two.
119
122
  Then, restart your app with `fastapi run`.
120
123
  It should resume the workflow from where it left off, completing step two without re-executing step one.
@@ -51,6 +51,9 @@ You can use DBOS to add reliable background jobs or cron scheduling or queues to
51
51
  Install and configure with:
52
52
 
53
53
  ```shell
54
+ python3 -m venv dbos-example/.venv
55
+ cd dbos-example
56
+ source .venv/bin/activate
54
57
  pip install dbos
55
58
  dbos init --config
56
59
  ```
@@ -76,7 +79,7 @@ def step_two():
76
79
  def dbos_workflow():
77
80
  step_one()
78
81
  for _ in range(5):
79
- print("Press Control + \ to stop the app...")
82
+ print("Press Control + C twice to stop the app...")
80
83
  DBOS.sleep(1)
81
84
  step_two()
82
85
 
@@ -87,7 +90,7 @@ def fastapi_endpoint():
87
90
 
88
91
  Save the program into `main.py` and start it with `fastapi run`.
89
92
  Visit `localhost:8000` in your browser to start the workflow.
90
- When prompted, press `Control + \` to force quit your application.
93
+ When prompted, press `Control + C` (You may need to press `Control + C` twice quickly, or press `Control + \`, if `Control + C` is not effective in your environment) to force quit your application.
91
94
  It should crash midway through the workflow, having completed step one but not step two.
92
95
  Then, restart your app with `fastapi run`.
93
96
  It should resume the workflow from where it left off, completing step two without re-executing step one.
@@ -5,14 +5,14 @@ Copyright (C) 2010-2012 Matsumoto Taichi.
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy of this
7
7
  software and associated documentation files (the "Software"), to deal in the Software
8
- without restriction, including without limitation the rights to use, copy, modify,
8
+ without restriction, including without limitation the rights to use, copy, modify,
9
9
  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
10
10
  persons to whom the Software is furnished to do so, subject to the following conditions:
11
11
 
12
12
  The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
14
14
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16
16
  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17
17
  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18
18
  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
@@ -2,7 +2,7 @@
2
2
  Add system tables.
3
3
 
4
4
  Revision ID: 5c361fc04708
5
- Revises:
5
+ Revises:
6
6
  Create Date: 2024-07-21 13:06:13.724602
7
7
  # mypy: allow-untyped-defs, allow-untyped-calls
8
8
  """
@@ -14,9 +14,7 @@ from typing import (
14
14
  Optional,
15
15
  Sequence,
16
16
  Set,
17
- Tuple,
18
17
  TypedDict,
19
- cast,
20
18
  )
21
19
 
22
20
  import psycopg
@@ -126,6 +124,7 @@ class GetWorkflowsInput:
126
124
  self.offset: Optional[int] = (
127
125
  None # Offset into the matching records for pagination
128
126
  )
127
+ self.sort_desc: bool = False # If true, sort by created_at in DESC order. Default false (in ASC order).
129
128
 
130
129
 
131
130
  class GetQueuedWorkflowsInput(TypedDict):
@@ -136,6 +135,7 @@ class GetQueuedWorkflowsInput(TypedDict):
136
135
  limit: Optional[int] # Return up to this many workflows IDs.
137
136
  offset: Optional[int] # Offset into the matching records for pagination
138
137
  name: Optional[str] # The name of the workflow function
138
+ sort_desc: Optional[bool] # Sort by created_at in DESC or ASC order
139
139
 
140
140
 
141
141
  class GetWorkflowsOutput:
@@ -149,25 +149,6 @@ class GetPendingWorkflowsOutput:
149
149
  self.queue_name: Optional[str] = queue_name
150
150
 
151
151
 
152
- class WorkflowInformation(TypedDict, total=False):
153
- workflow_uuid: str
154
- status: WorkflowStatuses # The status of the workflow.
155
- name: str # The name of the workflow function.
156
- workflow_class_name: str # The class name holding the workflow function.
157
- workflow_config_name: (
158
- str # The name of the configuration, if the class needs configuration
159
- )
160
- authenticated_user: str # The user who ran the workflow. Empty string if not set.
161
- assumed_role: str
162
- # The role used to run this workflow. Empty string if authorization is not required.
163
- authenticated_roles: List[str]
164
- # All roles the authenticated user has, if any.
165
- input: Optional[_serialization.WorkflowInputs]
166
- output: Optional[str]
167
- error: Optional[str]
168
- request: Optional[str]
169
-
170
-
171
152
  _dbos_null_topic = "__null__topic__"
172
153
  _buffer_flush_batch_size = 100
173
154
  _buffer_flush_interval_secs = 1.0
@@ -637,9 +618,11 @@ class SystemDatabase:
637
618
  return inputs
638
619
 
639
620
  def get_workflows(self, input: GetWorkflowsInput) -> GetWorkflowsOutput:
640
- query = sa.select(SystemSchema.workflow_status.c.workflow_uuid).order_by(
641
- SystemSchema.workflow_status.c.created_at.asc()
642
- )
621
+ query = sa.select(SystemSchema.workflow_status.c.workflow_uuid)
622
+ if input.sort_desc:
623
+ query = query.order_by(SystemSchema.workflow_status.c.created_at.desc())
624
+ else:
625
+ query = query.order_by(SystemSchema.workflow_status.c.created_at.asc())
643
626
  if input.name:
644
627
  query = query.where(SystemSchema.workflow_status.c.name == input.name)
645
628
  if input.authenticated_user:
@@ -683,15 +666,15 @@ class SystemDatabase:
683
666
  self, input: GetQueuedWorkflowsInput
684
667
  ) -> GetWorkflowsOutput:
685
668
 
686
- query = (
687
- sa.select(SystemSchema.workflow_queue.c.workflow_uuid)
688
- .join(
689
- SystemSchema.workflow_status,
690
- SystemSchema.workflow_queue.c.workflow_uuid
691
- == SystemSchema.workflow_status.c.workflow_uuid,
692
- )
693
- .order_by(SystemSchema.workflow_status.c.created_at.asc())
669
+ query = sa.select(SystemSchema.workflow_queue.c.workflow_uuid).join(
670
+ SystemSchema.workflow_status,
671
+ SystemSchema.workflow_queue.c.workflow_uuid
672
+ == SystemSchema.workflow_status.c.workflow_uuid,
694
673
  )
674
+ if input["sort_desc"]:
675
+ query = query.order_by(SystemSchema.workflow_status.c.created_at.desc())
676
+ else:
677
+ query = query.order_by(SystemSchema.workflow_status.c.created_at.asc())
695
678
 
696
679
  if input.get("name"):
697
680
  query = query.where(SystemSchema.workflow_status.c.name == input["name"])
@@ -2,7 +2,7 @@
2
2
  Initialize application database.
3
3
 
4
4
  Revision ID: c6b516e182b2
5
- Revises:
5
+ Revises:
6
6
  Create Date: 2024-07-31 18:06:42.500040
7
7
  """
8
8
 
@@ -49,6 +49,7 @@ def list_workflows(
49
49
  name: Optional[str] = None,
50
50
  limit: Optional[int] = None,
51
51
  offset: Optional[int] = None,
52
+ sort_desc: bool = False,
52
53
  ) -> List[WorkflowInformation]:
53
54
  input = GetWorkflowsInput()
54
55
  input.workflow_ids = workflow_ids
@@ -61,6 +62,7 @@ def list_workflows(
61
62
  input.limit = limit
62
63
  input.name = name
63
64
  input.offset = offset
65
+ input.sort_desc = sort_desc
64
66
 
65
67
  output: GetWorkflowsOutput = sys_db.get_workflows(input)
66
68
  infos: List[WorkflowInformation] = []
@@ -82,6 +84,7 @@ def list_queued_workflows(
82
84
  name: Optional[str] = None,
83
85
  request: bool = False,
84
86
  offset: Optional[int] = None,
87
+ sort_desc: bool = False,
85
88
  ) -> List[WorkflowInformation]:
86
89
  input: GetQueuedWorkflowsInput = {
87
90
  "queue_name": queue_name,
@@ -91,6 +94,7 @@ def list_queued_workflows(
91
94
  "limit": limit,
92
95
  "name": name,
93
96
  "offset": offset,
97
+ "sort_desc": sort_desc,
94
98
  }
95
99
  output: GetWorkflowsOutput = sys_db.get_queued_workflows(input)
96
100
  infos: List[WorkflowInformation] = []
@@ -27,7 +27,7 @@ dependencies = [
27
27
  ]
28
28
  requires-python = ">=3.9"
29
29
  readme = "README.md"
30
- version = "0.23.0a5"
30
+ version = "0.23.0a8"
31
31
 
32
32
  [project.license]
33
33
  text = "MIT"
@@ -5,14 +5,14 @@ Copyright (C) 2010-2012 Matsumoto Taichi
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy of this
7
7
  software and associated documentation files (the "Software"), to deal in the Software
8
- without restriction, including without limitation the rights to use, copy, modify,
8
+ without restriction, including without limitation the rights to use, copy, modify,
9
9
  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
10
10
  persons to whom the Software is furnished to do so, subject to the following conditions:
11
11
 
12
12
  The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
14
14
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16
16
  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17
17
  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18
18
  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
@@ -89,6 +89,13 @@ def test_list_workflow_limit(dbos: DBOS, sys_db: SystemDatabase) -> None:
89
89
  # Test all workflows appear
90
90
  outputs = _workflow_commands.list_workflows(sys_db)
91
91
  assert len(outputs) == num_workflows
92
+ for i, output in enumerate(outputs):
93
+ assert output.workflow_id == str(i)
94
+
95
+ # Test sort_desc inverts the order:
96
+ outputs = _workflow_commands.list_workflows(sys_db, sort_desc=True)
97
+ for i, output in enumerate(outputs):
98
+ assert output.workflow_id == str(num_workflows - i - 1)
92
99
 
93
100
  # Test LIMIT 2 returns the first two
94
101
  outputs = _workflow_commands.list_workflows(sys_db, limit=2)
@@ -235,6 +242,14 @@ def test_queued_workflows(dbos: DBOS, sys_db: SystemDatabase) -> None:
235
242
  assert workflow.updated_at is not None and workflow.updated_at > 0
236
243
  assert workflow.recovery_attempts == 1
237
244
 
245
+ # Test sort_desc inverts the order
246
+ workflows = _workflow_commands.list_queued_workflows(sys_db, sort_desc=True)
247
+ assert len(workflows) == queued_steps
248
+ for i, workflow in enumerate(workflows):
249
+ # Verify newest queue entries appear first
250
+ assert workflow.input is not None
251
+ assert workflow.input["args"][0] == queued_steps - i - 1
252
+
238
253
  # Verify list_workflows also properly lists the blocking steps
239
254
  workflows = _workflow_commands.list_workflows(sys_db)
240
255
  assert len(workflows) == queued_steps + 1
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes