Repository: getmoto/moto
Pull request #7981 (2024-08-15 08:31:04)

Admin: Improve scaffolding and AWS tests


Patch:
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index b42e4b90b..4df79b740 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -743,29 +743,20 @@
 
 ## bedrock-agent
 <details>
-<summary>18% implemented</summary>
+<summary>27% implemented</summary>
 
 - [ ] associate_agent_knowledge_base
 - [X] create_agent
 - [ ] create_agent_action_group
 - [ ] create_agent_alias
 - [ ] create_data_source
-- [ ] create_flow
-- [ ] create_flow_alias
-- [ ] create_flow_version
 - [X] create_knowledge_base
-- [ ] create_prompt
-- [ ] create_prompt_version
 - [X] delete_agent
 - [ ] delete_agent_action_group
 - [ ] delete_agent_alias
 - [ ] delete_agent_version
 - [ ] delete_data_source
-- [ ] delete_flow
-- [ ] delete_flow_alias
-- [ ] delete_flow_version
 - [X] delete_knowledge_base
-- [ ] delete_prompt
 - [ ] disassociate_agent_knowledge_base
 - [X] get_agent
 - [ ] get_agent_action_group
@@ -773,27 +764,18 @@
 - [ ] get_agent_knowledge_base
 - [ ] get_agent_version
 - [ ] get_data_source
-- [ ] get_flow
-- [ ] get_flow_alias
-- [ ] get_flow_version
 - [ ] get_ingestion_job
 - [X] get_knowledge_base
-- [ ] get_prompt
 - [ ] list_agent_action_groups
 - [ ] list_agent_aliases
 - [ ] list_agent_knowledge_bases
 - [ ] list_agent_versions
 - [X] list_agents
 - [ ] list_data_sources
-- [ ] list_flow_aliases
-- [ ] list_flow_versions
-- [ ] list_flows
 - [ ] list_ingestion_jobs
 - [X] list_knowledge_bases
-- [ ] list_prompts
 - [X] list_tags_for_resource
 - [ ] prepare_agent
-- [ ] prepare_flow
 - [ ] start_ingestion_job
 - [X] tag_resource
 - [X] untag_resource
@@ -802,10 +784,7 @@
 - [ ] update_agent_alias
 - [ ] update_agent_knowledge_base
 - [ ] update_data_source
-- [ ] update_flow
-- [ ] update_flow_alias
 - [ ] update_knowledge_base
-- [ ] update_prompt
 </details>
 
 ## budgets
@@ -1193,13 +1172,13 @@
 
 ## codebuild
 <details>
-<summary>16% implemented</summary>
+<summary>18% implemented</summary>
 
 - [ ] batch_delete_builds
 - [ ] batch_get_build_batches
 - [X] batch_get_builds
 - [ ] batch_get_fleets
-- [ ] batch_get_projects
+- [X] batch_get_projects
 - [ ] batch_get_report_groups
 - [ ] batch_get_reports
 - [ ] create_fleet
@@ -2129,7 +2108,7 @@
 
 ## dynamodb
 <details>
-<summary>63% implemented</summary>
+<summary>68% implemented</summary>
 
 - [X] batch_execute_statement
 - [X] batch_get_item
@@ -2139,7 +2118,7 @@
 - [X] create_table
 - [X] delete_backup
 - [X] delete_item
-- [ ] delete_resource_policy
+- [X] delete_resource_policy
 - [X] delete_table
 - [X] describe_backup
 - [X] describe_continuous_backups
@@ -2160,7 +2139,7 @@
 - [X] execute_transaction
 - [ ] export_table_to_point_in_time
 - [X] get_item
-- [ ] get_resource_policy
+- [X] get_resource_policy
 - [X] import_table
 - [X] list_backups
 - [ ] list_contributor_insights
@@ -2170,7 +2149,7 @@
 - [X] list_tables
 - [X] list_tags_of_resource
 - [X] put_item
-- [ ] put_resource_policy
+- [X] put_resource_policy
 - [X] query
 - [X] restore_table_from_backup
 - [X] restore_table_to_point_in_time
@@ -2292,7 +2271,6 @@
 - [ ] create_instance_export_task
 - [X] create_internet_gateway
 - [ ] create_ipam
-- [ ] create_ipam_external_resource_verification_token
 - [ ] create_ipam_pool
 - [ ] create_ipam_resource_discovery
 - [ ] create_ipam_scope
@@ -2369,7 +2347,6 @@
 - [ ] delete_instance_event_window
 - [X] delete_internet_gateway
 - [ ] delete_ipam
-- [ ] delete_ipam_external_resource_verification_token
 - [ ] delete_ipam_pool
 - [ ] delete_ipam_resource_discovery
 - [ ] delete_ipam_scope
@@ -2494,7 +2471,6 @@
 - [X] describe_instances
 - [X] describe_internet_gateways
 - [ ] describe_ipam_byoasn
-- [ ] describe_ipam_external_resource_verification_tokens
 - [ ] describe_ipam_pools
 - [ ] describe_ipam_resource_discoveries
 - [ ] describe_ipam_resource_discovery_associations
@@ -3305,7 +3281,7 @@
 
 ## emr
 <details>
-<summary>39% implemented</summary>
+<summary>42% implemented</summary>
 
 - [ ] add_instance_fleet
 - [X] add_instance_groups
@@ -3326,7 +3302,7 @@
 - [X] describe_step
 - [ ] describe_studio
 - [ ] get_auto_termination_policy
-- [ ] get_block_public_access_configuration
+- [X] get_block_public_access_configuration
 - [ ] get_cluster_session_credentials
 - [ ] get_managed_scaling_policy
 - [ ] get_studio_session_mapping
@@ -3347,7 +3323,7 @@
 - [X] modify_instance_groups
 - [X] put_auto_scaling_policy
 - [ ] put_auto_termination_policy
-- [ ] put_block_public_access_configuration
+- [X] put_block_public_access_configuration
 - [ ] put_managed_scaling_policy
 - [X] remove_auto_scaling_policy
 - [ ] remove_auto_termination_policy
@@ -6011,12 +5987,36 @@
 - [ ] synthesize_speech
 </details>
 
+## qldb
+<details>
+<summary>30% implemented</summary>
+
+- [ ] cancel_journal_kinesis_stream
+- [X] create_ledger
+- [X] delete_ledger
+- [ ] describe_journal_kinesis_stream
+- [ ] describe_journal_s3_export
+- [X] describe_ledger
+- [ ] export_journal_to_s3
+- [ ] get_block
+- [ ] get_digest
+- [ ] get_revision
+- [ ] list_journal_kinesis_streams_for_ledger
+- [ ] list_journal_s3_exports
+- [ ] list_journal_s3_exports_for_ledger
+- [ ] list_ledgers
+- [X] list_tags_for_resource
+- [ ] stream_journal_to_kinesis
+- [X] tag_resource
+- [ ] untag_resource
+- [X] update_ledger
+- [ ] update_ledger_permissions_mode
+</details>
+
 ## quicksight
 <details>
-<summary>7% implemented</summary>
+<summary>8% implemented</summary>
 
-- [ ] batch_create_topic_reviewed_answer
-- [ ] batch_delete_topic_reviewed_answer
 - [ ] cancel_ingestion
 - [ ] create_account_customization
 - [ ] create_account_subscription
@@ -6139,7 +6139,6 @@
 - [ ] list_theme_versions
 - [ ] list_themes
 - [ ] list_topic_refresh_schedules
-- [ ] list_topic_reviewed_answers
 - [ ] list_topics
 - [ ] list_user_groups
 - [X] list_users
@@ -7209,7 +7208,7 @@
 
 ## sagemaker
 <details>
-<summary>29% implemented</summary>
+<summary>33% implemented</summary>
 
 - [ ] add_association
 - [X] add_tags
@@ -7226,7 +7225,7 @@
 - [ ] create_code_repository
 - [X] create_compilation_job
 - [ ] create_context
-- [ ] create_data_quality_job_definition
+- [X] create_data_quality_job_definition
 - [ ] create_device_fleet
 - [X] create_domain
 - [ ] create_edge_deployment_plan
@@ -7249,8 +7248,8 @@
 - [ ] create_labeling_job
 - [ ] create_mlflow_tracking_server
 - [X] create_model
-- [ ] create_model_bias_job_definition
-- [ ] create_model_card
+- [X] create_model_bias_job_definition
+- [X] create_model_card
 - [ ] create_model_card_export_job
 - [X] create_model_explainability_job_definition
 - [X] create_model_package
@@ -7285,7 +7284,7 @@
 - [ ] delete_code_repository
 - [X] delete_compilation_job
 - [ ] delete_context
-- [ ] delete_data_quality_job_definition
+- [X] delete_data_quality_job_definition
 - [ ] delete_device_fleet
 - [X] delete_domain
 - [ ] delete_edge_deployment_plan
@@ -7306,8 +7305,8 @@
 - [ ] delete_inference_experiment
 - [ ] delete_mlflow_tracking_server
 - [X] delete_model
-- [ ] delete_model_bias_job_definition
-- [ ] delete_model_card
+- [X] delete_model_bias_job_definition
+- [X] delete_model_card
 - [X] delete_model_explainability_job_definition
 - [ ] delete_model_package
 - [ ] delete_model_package_group
@@ -7340,7 +7339,7 @@
 - [ ] describe_code_repository
 - [X] describe_compilation_job
 - [ ] describe_context
-- [ ] describe_data_quality_job_definition
+- [X] describe_data_quality_job_definition
 - [ ] describe_device
 - [ ] describe_device_fleet
 - [X] describe_domain
@@ -7365,8 +7364,8 @@
 - [ ] describe_lineage_group
 - [ ] describe_mlflow_tracking_server
 - [X] describe_model
-- [ ] describe_model_bias_job_definition
-- [ ] describe_model_card
+- [X] describe_model_bias_job_definition
+- [X] describe_model_card
 - [ ] describe_model_card_export_job
 - [X] describe_model_explainability_job_definition
 - [X] describe_model_package
@@ -7415,7 +7414,7 @@
 - [ ] list_code_repositories
 - [X] list_compilation_jobs
 - [ ] list_contexts
-- [ ] list_data_quality_job_definitions
+- [X] list_data_quality_job_definitions
 - [ ] list_device_fleets
 - [ ] list_devices
 - [X] list_domains
@@ -7441,10 +7440,10 @@
 - [ ] list_labeling_jobs_for_workteam
 - [ ] list_lineage_groups
 - [ ] list_mlflow_tracking_servers
-- [ ] list_model_bias_job_definitions
+- [X] list_model_bias_job_definitions
 - [ ] list_model_card_export_jobs
-- [ ] list_model_card_versions
-- [ ] list_model_cards
+- [X] list_model_card_versions
+- [X] list_model_cards
 - [X] list_model_explainability_job_definitions
 - [ ] list_model_metadata
 - [X] list_model_package_groups
@@ -7530,7 +7529,7 @@
 - [ ] update_inference_component_runtime_config
 - [ ] update_inference_experiment
 - [ ] update_mlflow_tracking_server
-- [ ] update_model_card
+- [X] update_model_card
 - [X] update_model_package
 - [ ] update_monitoring_alert
 - [ ] update_monitoring_schedule
@@ -8458,6 +8457,72 @@
 - [ ] update_vocabulary_filter
 </details>
 
+## transfer
+<details>
+<summary>13% implemented</summary>
+
+- [ ] create_access
+- [ ] create_agreement
+- [ ] create_connector
+- [ ] create_profile
+- [X] create_server
+- [X] create_user
+- [ ] create_workflow
+- [ ] delete_access
+- [ ] delete_agreement
+- [ ] delete_certificate
+- [ ] delete_connector
+- [ ] delete_host_key
+- [ ] delete_profile
+- [X] delete_server
+- [X] delete_ssh_public_key
+- [X] delete_user
+- [ ] delete_workflow
+- [ ] describe_access
+- [ ] describe_agreement
+- [ ] describe_certificate
+- [ ] describe_connector
+- [ ] describe_execution
+- [ ] describe_host_key
+- [ ] describe_profile
+- [ ] describe_security_policy
+- [X] describe_server
+- [X] describe_user
+- [ ] describe_workflow
+- [ ] import_certificate
+- [ ] import_host_key
+- [X] import_ssh_public_key
+- [ ] list_accesses
+- [ ] list_agreements
+- [ ] list_certificates
+- [ ] list_connectors
+- [ ] list_executions
+- [ ] list_host_keys
+- [ ] list_profiles
+- [ ] list_security_policies
+- [ ] list_servers
+- [ ] list_tags_for_resource
+- [ ] list_users
+- [ ] list_workflows
+- [ ] send_workflow_step_state
+- [ ] start_directory_listing
+- [ ] start_file_transfer
+- [ ] start_server
+- [ ] stop_server
+- [ ] tag_resource
+- [ ] test_connection
+- [ ] test_identity_provider
+- [ ] untag_resource
+- [ ] update_access
+- [ ] update_agreement
+- [ ] update_certificate
+- [ ] update_connector
+- [ ] update_host_key
+- [ ] update_profile
+- [ ] update_server
+- [ ] update_user
+</details>
+
 ## wafv2
 <details>
 <summary>38% implemented</summary>
@@ -8809,7 +8874,6 @@
 - qapps
 - qbusiness
 - qconnect
-- qldb
 - qldb-session
 - rbin
 - redshift-serverless
@@ -8851,7 +8915,6 @@
 - timestream-influxdb
 - timestream-query
 - tnb
-- transfer
 - translate
 - trustedadvisor
 - verifiedpermissions
diff --git a/docs/docs/services/acm-pca.rst b/docs/docs/services/acm-pca.rst
index 61ee344d7..9a7d25dac 100644
--- a/docs/docs/services/acm-pca.rst
+++ b/docs/docs/services/acm-pca.rst
@@ -39,7 +39,7 @@ acm-pca
 - [X] import_certificate_authority_certificate
 - [X] issue_certificate
   
-        The following parameters are not yet implemented: ApiPassthrough, SigningAlgorithm, TemplateArn, Validity, ValidityNotBefore, IdempotencyToken
+        The following parameters are not yet implemented: ApiPassthrough, SigningAlgorithm, Validity, ValidityNotBefore, IdempotencyToken
         Some fields of the resulting certificate will have default values, instead of using the CSR
         
 
diff --git a/docs/docs/services/bedrock-agent.rst b/docs/docs/services/bedrock-agent.rst
index 1a37a5bc2..dbf3f6b31 100644
--- a/docs/docs/services/bedrock-agent.rst
+++ b/docs/docs/services/bedrock-agent.rst
@@ -21,22 +21,13 @@ bedrock-agent
 - [ ] create_agent_action_group
 - [ ] create_agent_alias
 - [ ] create_data_source
-- [ ] create_flow
-- [ ] create_flow_alias
-- [ ] create_flow_version
 - [X] create_knowledge_base
-- [ ] create_prompt
-- [ ] create_prompt_version
 - [X] delete_agent
 - [ ] delete_agent_action_group
 - [ ] delete_agent_alias
 - [ ] delete_agent_version
 - [ ] delete_data_source
-- [ ] delete_flow
-- [ ] delete_flow_alias
-- [ ] delete_flow_version
 - [X] delete_knowledge_base
-- [ ] delete_prompt
 - [ ] disassociate_agent_knowledge_base
 - [X] get_agent
 - [ ] get_agent_action_group
@@ -44,27 +35,18 @@ bedrock-agent
 - [ ] get_agent_knowledge_base
 - [ ] get_agent_version
 - [ ] get_data_source
-- [ ] get_flow
-- [ ] get_flow_alias
-- [ ] get_flow_version
 - [ ] get_ingestion_job
 - [X] get_knowledge_base
-- [ ] get_prompt
 - [ ] list_agent_action_groups
 - [ ] list_agent_aliases
 - [ ] list_agent_knowledge_bases
 - [ ] list_agent_versions
 - [X] list_agents
 - [ ] list_data_sources
-- [ ] list_flow_aliases
-- [ ] list_flow_versions
-- [ ] list_flows
 - [ ] list_ingestion_jobs
 - [X] list_knowledge_bases
-- [ ] list_prompts
 - [X] list_tags_for_resource
 - [ ] prepare_agent
-- [ ] prepare_flow
 - [ ] start_ingestion_job
 - [X] tag_resource
 - [X] untag_resource
@@ -73,8 +55,5 @@ bedrock-agent
 - [ ] update_agent_alias
 - [ ] update_agent_knowledge_base
 - [ ] update_data_source
-- [ ] update_flow
-- [ ] update_flow_alias
 - [ ] update_knowledge_base
-- [ ] update_prompt
 
diff --git a/docs/docs/services/cf.rst b/docs/docs/services/cf.rst
index 599c8148a..1beea531d 100644
--- a/docs/docs/services/cf.rst
+++ b/docs/docs/services/cf.rst
@@ -175,8 +175,22 @@ Please let us know if you'd like support for a resource not yet listed here.
   +---------------------------------------+--------+--------+--------+----------------------------------------+
   |AWS::RDS::DBClusterParameterGroup      |        |        |        |                                        |
   +---------------------------------------+--------+--------+--------+----------------------------------------+
+  |AWS::RDS::DBInstance                   |    x   |        |        | - [ ] DBSystemId                       |
+  +---------------------------------------+--------+--------+--------+ - [x] Endpoint.Address                 |
+  |                                       |        |        |        | - [ ] DBInstanceArn                    |
+  +---------------------------------------+--------+--------+--------+ - [ ] CertificateDetails.ValidTill     |
+  |                                       |        |        |        | - [ ] CertificateDetails.CAIdentifier  |
+  +---------------------------------------+--------+--------+--------+ - [x] Endpoint.Port                    |
+  |                                       |        |        |        | - [ ] Endpoint.HostedZoneId            |
+  +---------------------------------------+--------+--------+--------+ - [ ] DbiResourceId                    |
+  |                                       |        |        |        | - [ ] MasterUserSecret.SecretArn       |
+  +---------------------------------------+--------+--------+--------+----------------------------------------+
   |AWS::RDS::DBParameterGroup             |    x   |        |        | - [ ] DBParameterGroupName             |
   +---------------------------------------+--------+--------+--------+----------------------------------------+
+  |AWS::RDS::DBSecurityGroup              |    x   |        |        |                                        |
+  +---------------------------------------+--------+--------+--------+----------------------------------------+
+  |AWS::RDS::DBSubnetGroup                |    x   |        |        |                                        |
+  +---------------------------------------+--------+--------+--------+----------------------------------------+
   |AWS::Redshift::Cluster                 |    x   |        |        | - [ ] MasterPasswordSecretArn          |
   +---------------------------------------+--------+--------+--------+ - [x] Endpoint.Address                 |
   |                                       |        |        |        | - [x] Endpoint.Port                    |
diff --git a/docs/docs/services/codebuild.rst b/docs/docs/services/codebuild.rst
index 1db156245..b0d70fa9b 100644
--- a/docs/docs/services/codebuild.rst
+++ b/docs/docs/services/codebuild.rst
@@ -18,7 +18,7 @@ codebuild
 - [ ] batch_get_build_batches
 - [X] batch_get_builds
 - [ ] batch_get_fleets
-- [ ] batch_get_projects
+- [X] batch_get_projects
 - [ ] batch_get_report_groups
 - [ ] batch_get_reports
 - [ ] create_fleet
diff --git a/docs/docs/services/dynamodb.rst b/docs/docs/services/dynamodb.rst
index 777a3faef..81061f6b3 100644
--- a/docs/docs/services/dynamodb.rst
+++ b/docs/docs/services/dynamodb.rst
@@ -26,7 +26,7 @@ dynamodb
 - [X] create_table
 - [X] delete_backup
 - [X] delete_item
-- [ ] delete_resource_policy
+- [X] delete_resource_policy
 - [X] delete_table
 - [X] describe_backup
 - [X] describe_continuous_backups
@@ -57,7 +57,7 @@ dynamodb
 
 - [ ] export_table_to_point_in_time
 - [X] get_item
-- [ ] get_resource_policy
+- [X] get_resource_policy
 - [X] import_table
   
         Only InputFormat=DYNAMODB_JSON is supported so far.
@@ -73,7 +73,7 @@ dynamodb
 - [X] list_tables
 - [X] list_tags_of_resource
 - [X] put_item
-- [ ] put_resource_policy
+- [X] put_resource_policy
 - [X] query
 - [X] restore_table_from_backup
 - [X] restore_table_to_point_in_time
diff --git a/docs/docs/services/ec2.rst b/docs/docs/services/ec2.rst
index 81c97c4eb..fdba00c93 100644
--- a/docs/docs/services/ec2.rst
+++ b/docs/docs/services/ec2.rst
@@ -92,7 +92,6 @@ ec2
 - [ ] create_instance_export_task
 - [X] create_internet_gateway
 - [ ] create_ipam
-- [ ] create_ipam_external_resource_verification_token
 - [ ] create_ipam_pool
 - [ ] create_ipam_resource_discovery
 - [ ] create_ipam_scope
@@ -173,7 +172,6 @@ ec2
 - [ ] delete_instance_event_window
 - [X] delete_internet_gateway
 - [ ] delete_ipam
-- [ ] delete_ipam_external_resource_verification_token
 - [ ] delete_ipam_pool
 - [ ] delete_ipam_resource_discovery
 - [ ] delete_ipam_scope
@@ -311,7 +309,6 @@ ec2
 - [X] describe_instances
 - [X] describe_internet_gateways
 - [ ] describe_ipam_byoasn
-- [ ] describe_ipam_external_resource_verification_tokens
 - [ ] describe_ipam_pools
 - [ ] describe_ipam_resource_discoveries
 - [ ] describe_ipam_resource_discovery_associations
diff --git a/docs/docs/services/emr.rst b/docs/docs/services/emr.rst
index 9ec42c266..9d88e7174 100644
--- a/docs/docs/services/emr.rst
+++ b/docs/docs/services/emr.rst
@@ -33,7 +33,7 @@ emr
 - [X] describe_step
 - [ ] describe_studio
 - [ ] get_auto_termination_policy
-- [ ] get_block_public_access_configuration
+- [X] get_block_public_access_configuration
 - [ ] get_cluster_session_credentials
 - [ ] get_managed_scaling_policy
 - [ ] get_studio_session_mapping
@@ -54,7 +54,7 @@ emr
 - [X] modify_instance_groups
 - [X] put_auto_scaling_policy
 - [ ] put_auto_termination_policy
-- [ ] put_block_public_access_configuration
+- [X] put_block_public_access_configuration
 - [ ] put_managed_scaling_policy
 - [X] remove_auto_scaling_policy
 - [ ] remove_auto_termination_policy
diff --git a/docs/docs/services/qldb.rst b/docs/docs/services/qldb.rst
new file mode 100644
index 000000000..b3e467da4
--- /dev/null
+++ b/docs/docs/services/qldb.rst
@@ -0,0 +1,39 @@
+.. _implementedservice_qldb:
+
+.. |start-h3| raw:: html
+
+    <h3>
+
+.. |end-h3| raw:: html
+
+    </h3>
+
+====
+qldb
+====
+
+.. autoclass:: moto.qldb.models.QLDBBackend
+
+|start-h3| Implemented features for this service |end-h3|
+
+- [ ] cancel_journal_kinesis_stream
+- [X] create_ledger
+- [X] delete_ledger
+- [ ] describe_journal_kinesis_stream
+- [ ] describe_journal_s3_export
+- [X] describe_ledger
+- [ ] export_journal_to_s3
+- [ ] get_block
+- [ ] get_digest
+- [ ] get_revision
+- [ ] list_journal_kinesis_streams_for_ledger
+- [ ] list_journal_s3_exports
+- [ ] list_journal_s3_exports_for_ledger
+- [ ] list_ledgers
+- [X] list_tags_for_resource
+- [ ] stream_journal_to_kinesis
+- [X] tag_resource
+- [ ] untag_resource
+- [X] update_ledger
+- [ ] update_ledger_permissions_mode
+
diff --git a/docs/docs/services/quicksight.rst b/docs/docs/services/quicksight.rst
index b870ef87f..0ae53fcc2 100644
--- a/docs/docs/services/quicksight.rst
+++ b/docs/docs/services/quicksight.rst
@@ -16,8 +16,6 @@ quicksight
 
 |start-h3| Implemented features for this service |end-h3|
 
-- [ ] batch_create_topic_reviewed_answer
-- [ ] batch_delete_topic_reviewed_answer
 - [ ] cancel_ingestion
 - [ ] create_account_customization
 - [ ] create_account_subscription
@@ -148,7 +146,6 @@ quicksight
 - [ ] list_theme_versions
 - [ ] list_themes
 - [ ] list_topic_refresh_schedules
-- [ ] list_topic_reviewed_answers
 - [ ] list_topics
 - [ ] list_user_groups
 - [X] list_users
diff --git a/docs/docs/services/sagemaker.rst b/docs/docs/services/sagemaker.rst
index 55571cd43..3112ac981 100644
--- a/docs/docs/services/sagemaker.rst
+++ b/docs/docs/services/sagemaker.rst
@@ -29,7 +29,7 @@ sagemaker
 - [ ] create_code_repository
 - [X] create_compilation_job
 - [ ] create_context
-- [ ] create_data_quality_job_definition
+- [X] create_data_quality_job_definition
 - [ ] create_device_fleet
 - [X] create_domain
 - [ ] create_edge_deployment_plan
@@ -52,8 +52,8 @@ sagemaker
 - [ ] create_labeling_job
 - [ ] create_mlflow_tracking_server
 - [X] create_model
-- [ ] create_model_bias_job_definition
-- [ ] create_model_card
+- [X] create_model_bias_job_definition
+- [X] create_model_card
 - [ ] create_model_card_export_job
 - [X] create_model_explainability_job_definition
 - [X] create_model_package
@@ -88,7 +88,7 @@ sagemaker
 - [ ] delete_code_repository
 - [X] delete_compilation_job
 - [ ] delete_context
-- [ ] delete_data_quality_job_definition
+- [X] delete_data_quality_job_definition
 - [ ] delete_device_fleet
 - [X] delete_domain
 - [ ] delete_edge_deployment_plan
@@ -109,8 +109,8 @@ sagemaker
 - [ ] delete_inference_experiment
 - [ ] delete_mlflow_tracking_server
 - [X] delete_model
-- [ ] delete_model_bias_job_definition
-- [ ] delete_model_card
+- [X] delete_model_bias_job_definition
+- [X] delete_model_card
 - [X] delete_model_explainability_job_definition
 - [ ] delete_model_package
 - [ ] delete_model_package_group
@@ -143,7 +143,7 @@ sagemaker
 - [ ] describe_code_repository
 - [X] describe_compilation_job
 - [ ] describe_context
-- [ ] describe_data_quality_job_definition
+- [X] describe_data_quality_job_definition
 - [ ] describe_device
 - [ ] describe_device_fleet
 - [X] describe_domain
@@ -168,8 +168,8 @@ sagemaker
 - [ ] describe_lineage_group
 - [ ] describe_mlflow_tracking_server
 - [X] describe_model
-- [ ] describe_model_bias_job_definition
-- [ ] describe_model_card
+- [X] describe_model_bias_job_definition
+- [X] describe_model_card
 - [ ] describe_model_card_export_job
 - [X] describe_model_explainability_job_definition
 - [X] describe_model_package
@@ -218,7 +218,7 @@ sagemaker
 - [ ] list_code_repositories
 - [X] list_compilation_jobs
 - [ ] list_contexts
-- [ ] list_data_quality_job_definitions
+- [X] list_data_quality_job_definitions
 - [ ] list_device_fleets
 - [ ] list_devices
 - [X] list_domains
@@ -244,10 +244,10 @@ sagemaker
 - [ ] list_labeling_jobs_for_workteam
 - [ ] list_lineage_groups
 - [ ] list_mlflow_tracking_servers
-- [ ] list_model_bias_job_definitions
+- [X] list_model_bias_job_definitions
 - [ ] list_model_card_export_jobs
-- [ ] list_model_card_versions
-- [ ] list_model_cards
+- [X] list_model_card_versions
+- [X] list_model_cards
 - [X] list_model_explainability_job_definitions
 - [ ] list_model_metadata
 - [X] list_model_package_groups
@@ -338,7 +338,7 @@ sagemaker
 - [ ] update_inference_component_runtime_config
 - [ ] update_inference_experiment
 - [ ] update_mlflow_tracking_server
-- [ ] update_model_card
+- [X] update_model_card
 - [X] update_model_package
 - [ ] update_monitoring_alert
 - [ ] update_monitoring_schedule
diff --git a/docs/docs/services/transfer.rst b/docs/docs/services/transfer.rst
new file mode 100644
index 000000000..2d1a3d6a4
--- /dev/null
+++ b/docs/docs/services/transfer.rst
@@ -0,0 +1,79 @@
+.. _implementedservice_transfer:
+
+.. |start-h3| raw:: html
+
+    <h3>
+
+.. |end-h3| raw:: html
+
+    </h3>
+
+========
+transfer
+========
+
+.. autoclass:: moto.transfer.models.TransferBackend
+
+|start-h3| Implemented features for this service |end-h3|
+
+- [ ] create_access
+- [ ] create_agreement
+- [ ] create_connector
+- [ ] create_profile
+- [X] create_server
+- [X] create_user
+- [ ] create_workflow
+- [ ] delete_access
+- [ ] delete_agreement
+- [ ] delete_certificate
+- [ ] delete_connector
+- [ ] delete_host_key
+- [ ] delete_profile
+- [X] delete_server
+- [X] delete_ssh_public_key
+- [X] delete_user
+- [ ] delete_workflow
+- [ ] describe_access
+- [ ] describe_agreement
+- [ ] describe_certificate
+- [ ] describe_connector
+- [ ] describe_execution
+- [ ] describe_host_key
+- [ ] describe_profile
+- [ ] describe_security_policy
+- [X] describe_server
+- [X] describe_user
+- [ ] describe_workflow
+- [ ] import_certificate
+- [ ] import_host_key
+- [X] import_ssh_public_key
+- [ ] list_accesses
+- [ ] list_agreements
+- [ ] list_certificates
+- [ ] list_connectors
+- [ ] list_executions
+- [ ] list_host_keys
+- [ ] list_profiles
+- [ ] list_security_policies
+- [ ] list_servers
+- [ ] list_tags_for_resource
+- [ ] list_users
+- [ ] list_workflows
+- [ ] send_workflow_step_state
+- [ ] start_directory_listing
+- [ ] start_file_transfer
+- [ ] start_server
+- [ ] stop_server
+- [ ] tag_resource
+- [ ] test_connection
+- [ ] test_identity_provider
+- [ ] untag_resource
+- [ ] update_access
+- [ ] update_agreement
+- [ ] update_certificate
+- [ ] update_connector
+- [ ] update_host_key
+- [ ] update_profile
+- [ ] update_server
+- [ ] update_user
+
diff --git a/moto/backend_index.py b/moto/backend_index.py
index da9c99646..0b7a0c59c 100644
--- a/moto/backend_index.py
+++ b/moto/backend_index.py
@@ -130,6 +130,7 @@ backend_url_patterns = [
     ("personalize", re.compile("https?://personalize\\.(.+)\\.amazonaws\\.com")),
     ("pinpoint", re.compile("https?://pinpoint\\.(.+)\\.amazonaws\\.com")),
     ("polly", re.compile("https?://polly\\.(.+)\\.amazonaws.com")),
+    ("qldb", re.compile("https?://qldb\\.(.+)\\.amazonaws\\.com")),
     ("quicksight", re.compile("https?://quicksight\\.(.+)\\.amazonaws\\.com")),
     ("ram", re.compile("https?://ram\\.(.+)\\.amazonaws.com")),
     ("rds", re.compile("https?://rds\\.(.+)\\.amazonaws\\.com")),
diff --git a/moto/backends.py b/moto/backends.py
index 884c3bedc..d66121c36 100644
--- a/moto/backends.py
+++ b/moto/backends.py
@@ -102,6 +102,7 @@ if TYPE_CHECKING:
     from moto.personalize.models import PersonalizeBackend
     from moto.pinpoint.models import PinpointBackend
     from moto.polly.models import PollyBackend
+    from moto.qldb.models import QLDBBackend
     from moto.quicksight.models import QuickSightBackend
     from moto.ram.models import ResourceAccessManagerBackend
     from moto.rds.models import RDSBackend
@@ -276,6 +277,7 @@ SERVICE_NAMES = Union[
     "Literal['personalize']",
     "Literal['pinpoint']",
     "Literal['polly']",
+    "Literal['qldb']",
     "Literal['quicksight']",
     "Literal['ram']",
     "Literal['rds']",
@@ -590,6 +592,8 @@ def get_backend(name: "Literal['pinpoint']") -> "BackendDict[PinpointBackend]":
 @overload
 def get_backend(name: "Literal['polly']") -> "BackendDict[PollyBackend]": ...
 @overload
+def get_backend(name: "Literal['qldb']") -> "BackendDict[QLDBBackend]": ...
+@overload
 def get_backend(name: "Literal['quicksight']") -> "BackendDict[QuickSightBackend]": ...
 @overload
 def get_backend(
diff --git a/moto/events/models.py b/moto/events/models.py
index f0a4f895d..1efe0e359 100644
--- a/moto/events/models.py
+++ b/moto/events/models.py
@@ -29,6 +29,7 @@ from moto.events.exceptions import (
     ValidationException,
 )
 from moto.moto_api._internal import mock_random as random
+from moto.secretsmanager import secretsmanager_backends
 from moto.utilities.arns import parse_arn
 from moto.utilities.paginator import paginate
 from moto.utilities.tagging_service import TaggingService
@@ -766,7 +767,21 @@ class Connection(BaseModel):
         self.creation_time = unix_time()
         self.state = "AUTHORIZED"
 
-        self.arn = f"arn:{get_partition(region_name)}:events:{region_name}:{account_id}:connection/{self.name}/{self.uuid}"
+        connection_id = f"{self.name}/{self.uuid}"
+        secretsmanager_backend = secretsmanager_backends[account_id][region_name]
+        secret = secretsmanager_backend.create_secret(
+            name=f"{connection_id}/auth",
+            secret_string=json.dumps(self.auth_parameters),
+            replica_regions=list(),
+            force_overwrite=False,
+            secret_binary=None,
+            description=f"Auth parameters for Eventbridge connection {connection_id}",
+            tags=list(),
+            kms_key_id=None,
+            client_request_token=None,
+        )
+        self.secret_arn = json.loads(secret).get("ARN")
+        self.arn = f"arn:{get_partition(region_name)}:events:{region_name}:{account_id}:connection/{connection_id}"
 
     def describe_short(self) -> Dict[str, Any]:
         """
@@ -818,6 +833,7 @@ class Connection(BaseModel):
             "CreationTime": self.creation_time,
             "Description": self.description,
             "Name": self.name,
+            "SecretArn": self.secret_arn,
         }
 
 
diff --git a/moto/qldb/__init__.py b/moto/qldb/__init__.py
new file mode 100644
index 000000000..e606e6a09
--- /dev/null
+++ b/moto/qldb/__init__.py
@@ -0,0 +1,1 @@
+from .models import qldb_backends  # noqa: F401
diff --git a/moto/qldb/exceptions.py b/moto/qldb/exceptions.py
new file mode 100644
index 000000000..7f9954a03
--- /dev/null
+++ b/moto/qldb/exceptions.py
@@ -0,0 +1,31 @@
+"""Exceptions raised by the qldb service."""
+
+from moto.core.exceptions import JsonRESTError
+
+
+class QLDBError(JsonRESTError):
+    code = 400
+
+
+class LedgerNotFoundException(QLDBError):
+    def __init__(self, name: str) -> None:
+        super().__init__(
+            "LedgerNotFound",
+            f"{name} does not match any existing ledger.",
+        )
+
+
+class LedgerNameAlreadyTakenException(QLDBError):
+    def __init__(self, name: str) -> None:
+        super().__init__(
+            "LedgerNameAlreadyTaken",
+            f"There is already a ledger with the name '{name}'",
+        )
+
+
+class ResourceNotFoundException(QLDBError):
+    def __init__(self, resource_arn: str) -> None:
+        super().__init__(
+            "ResourceNotFound",
+            f"{resource_arn} does not match the resource arn of any existing ledger",
+        )
diff --git a/moto/qldb/models.py b/moto/qldb/models.py
new file mode 100644
index 000000000..ef2084f93
--- /dev/null
+++ b/moto/qldb/models.py
@@ -0,0 +1,152 @@
+"""QLDBBackend class with methods for supported APIs."""
+
+from datetime import datetime
+from typing import Any, Dict, Optional, Tuple, Union
+
+from moto.core.base_backend import BackendDict, BaseBackend
+from moto.qldb.exceptions import (
+    LedgerNameAlreadyTakenException,
+    LedgerNotFoundException,
+    ResourceNotFoundException,
+)
+
+
+class QLDBBackend(BaseBackend):
+    """Implementation of QLDB APIs."""
+
+    def __init__(self, region_name: str, account_id: str) -> None:
+        super().__init__(region_name, account_id)
+        self.ledgers: Dict[str, Any] = dict()  # type ignore[misc]
+
+    def describe_ledger(
+        self, name: str
+    ) -> Tuple[
+        str,
+        Optional[str],
+        Optional[str],
+        Optional[datetime],
+        Optional[str],
+        Optional[bool],
+        Optional[Dict[str, Union[str, datetime]]],
+    ]:
+        if name not in self.ledgers:
+            raise LedgerNotFoundException(name)
+        ledger = self.ledgers[name]
+        return (
+            name,
+            ledger.get("arn"),
+            ledger.get("state"),
+            ledger.get("creation_date_time"),
+            ledger.get("permissions_mode"),
+            ledger.get("deletion_protection"),
+            ledger.get("encryption_description"),
+        )
+
+    def _get_kms_key_arn(self, key: str) -> str:
+        return f"arn:aws:kms:us-east-1:123456789012:key/{key}"
+
+    def create_ledger(
+        self,
+        name: str,
+        tags: Optional[Dict[str, str]],
+        permissions_mode: str,
+        deletion_protection: Optional[bool],
+        kms_key: Optional[str],
+    ) -> Tuple[
+        str,
+        str,
+        str,
+        datetime,
+        str,
+        Optional[bool],
+        str,
+    ]:
+        if name in self.ledgers:
+            raise LedgerNameAlreadyTakenException(name)
+        kms_key_arn = self._get_kms_key_arn(kms_key or name)
+        encryption_description = {
+            "kms_key_arn": kms_key_arn,
+            "encryption_status": "ENABLED",
+            "inaccessible_kms_key_date_time": datetime.now().strftime(
+                "%d/%m/%Y, %H:%M:%S"
+            ),
+        }
+        arn = f"arn:aws:qldb:us-east-1:123456789012:ledger/{name}"
+        creation_date_time = datetime.now()
+        state = "ACTIVE"
+        ledger = {
+            "name": name,
+            "arn": arn,
+            "state": state,
+            "creation_date_time": creation_date_time,
+            "permissions_mode": permissions_mode,
+            "deletion_protection": deletion_protection,
+            "encryption_description": encryption_description,
+            "tags": tags,
+        }
+        self.ledgers[name] = ledger
+        return (
+            name,
+            arn,
+            state,
+            creation_date_time,
+            permissions_mode,
+            deletion_protection,
+            kms_key_arn,
+        )
+
+    def delete_ledger(self, name: str) -> None:
+        if name not in self.ledgers:
+            raise LedgerNotFoundException(name)
+        del self.ledgers[name]
+        return
+
+    def update_ledger(
+        self, name: str, deletion_protection: Optional[bool], kms_key: Optional[str]
+    ) -> Tuple[
+        str,
+        Optional[str],
+        Optional[str],
+        Optional[datetime],
+        Optional[bool],
+        Optional[Dict[str, Union[str, datetime]]],
+    ]:  # type: ignore[misc]
+        if name not in self.ledgers:
+            raise LedgerNotFoundException(name)
+        if deletion_protection is not None:
+            self.ledgers[name]["deletion_protection"] = deletion_protection
+        if kms_key and "encryption_description" in self.ledgers[name]:
+            self.ledgers[name]["encryption_description"]["kms_key_arn"] = (
+                self._get_kms_key_arn(kms_key)
+            )
+
+        ledger = self.ledgers[name]
+
+        return (
+            name,
+            ledger.get("arn"),
+            ledger.get("state"),
+            ledger.get("creation_date_time"),
+            ledger.get("deletion_protection"),
+            ledger.get("encryption_description"),
+        )
+
+    def _get_ledger_by_resource_arn(self, resource_arn: str) -> Dict[str, Any]:  # type: ignore[misc]
+        for ledger in self.ledgers.values():
+            if ledger.get("resource_arn") == resource_arn:
+                return ledger
+        raise ResourceNotFoundException(resource_arn)
+
+    def tag_resource(self, resource_arn: str, new_tags: Dict[str, str]) -> None:
+        ledger = self._get_ledger_by_resource_arn(resource_arn)
+        tags = ledger.get("tags") or {}
+        tags.update(new_tags)
+        ledger["tags"] = tags
+        return
+
+    def list_tags_for_resource(self, resource_arn: str) -> Dict[str, str]:
+        ledger = self._get_ledger_by_resource_arn(resource_arn)
+        return ledger.get("tags") or {}
+
+
+qldb_backends = BackendDict(QLDBBackend, "qldb")
diff --git a/moto/qldb/responses.py b/moto/qldb/responses.py
new file mode 100644
index 000000000..47a487f51
--- /dev/null
+++ b/moto/qldb/responses.py
@@ -0,0 +1,157 @@
+"""Handles incoming qldb requests, invokes methods, returns responses."""
+
+import json
+from datetime import datetime
+from typing import Dict, Optional, Union
+
+from moto.core.responses import BaseResponse
+
+from .models import QLDBBackend, qldb_backends
+
+
+class QLDBResponse(BaseResponse):
+    """Handler for QLDB requests and responses."""
+
+    def __init__(self) -> None:
+        super().__init__(service_name="qldb")
+
+    @property
+    def qldb_backend(self) -> QLDBBackend:
+        """Return backend instance specific for this region."""
+        return qldb_backends[self.current_account][self.region]
+
+    def _format_encryption_description(
+        self, encryption_description: Optional[Dict[str, Union[str, datetime]]]
+    ) -> Dict[str, Optional[Union[str, datetime]]]:
+        return {
+            "KmsKeyArn": (encryption_description or {}).get("kms_key_arn"),
+            "EncryptionStatus": (encryption_description or {}).get("encryption_status"),
+            "InaccessibleKmsKeyDateTime": (encryption_description or {}).get(
+                "inaccessible_kms_key_date_time"
+            ),
+        }
+
+    def describe_ledger(self) -> str:
+        name = self._get_param("name")
+        (
+            name,
+            arn,
+            state,
+            creation_date_time,
+            permissions_mode,
+            deletion_protection,
+            encryption_description,
+        ) = self.qldb_backend.describe_ledger(
+            name=name,
+        )
+        creation_date_time_string = (
+            creation_date_time.strftime("%d/%m/%Y, %H:%M:%S")
+            if creation_date_time is not None
+            else ""
+        )
+        return json.dumps(
+            dict(
+                Name=name,
+                Arn=arn,
+                State=state,
+                CreationDateTime=creation_date_time_string,
+                PermissionsMode=permissions_mode,
+                DeletionProtection=deletion_protection,
+                EncryptionDescription=self._format_encryption_description(
+                    encryption_description
+                ),
+            )
+        )
+
+    def create_ledger(self) -> str:
+        params = json.loads(self.body)
+        name = params.get("Name")
+        tags = params.get("Tags")
+        permissions_mode = params.get("PermissionsMode")
+        deletion_protection = params.get("DeletionProtection")
+        kms_key = params.get("KmsKey")
+        (
+            name,
+            arn,
+            state,
+            creation_date_time,
+            permissions_mode,
+            deletion_protection,
+            kms_key_arn,
+        ) = self.qldb_backend.create_ledger(  # type: ignore[misc]
+            name=name,
+            tags=tags,
+            permissions_mode=permissions_mode,
+            deletion_protection=deletion_protection,
+            kms_key=kms_key,
+        )
+        return json.dumps(
+            dict(
+                Name=name,
+                Arn=arn,
+                State=state,
+                CreationDateTime=creation_date_time.strftime("%d/%m/%Y, %H:%M:%S"),
+                PermissionsMode=permissions_mode,
+                DeletionProtection=deletion_protection,
+                KmsKeyArn=kms_key_arn,
+            )
+        )
+
+    def delete_ledger(self) -> str:
+        name = self._get_param("name")
+        self.qldb_backend.delete_ledger(
+            name=name,
+        )
+        return json.dumps(dict())
+
+    def update_ledger(self) -> str:
+        name = self._get_param("name")
+        params = json.loads(self.body)
+        deletion_protection = params.get("DeletionProtection")
+        kms_key = params.get("KmsKey")
+        (
+            name,
+            arn,
+            state,
+            creation_date_time,
+            deletion_protection,
+            encryption_description,
+        ) = self.qldb_backend.update_ledger(
+            name=name,
+            deletion_protection=deletion_protection,
+            kms_key=kms_key,
+        )
+        creation_date_time_string = (
+            creation_date_time.strftime("%d/%m/%Y, %H:%M:%S")
+            if creation_date_time is not None
+            else ""
+        )
+        return json.dumps(
+            dict(
+                name=name,
+                arn=arn,
+                state=state,
+                creationDateTime=creation_date_time_string,
+                deletionProtection=deletion_protection,
+                encryptionDescription=self._format_encryption_description(
+                    encryption_description
+                ),
+            )
+        )
+
+    def tag_resource(self) -> str:
+        params = json.loads(self.body)
+        resource_arn = params.get("ResourceArn")
+        tags = params.get("Tags")
+        self.qldb_backend.tag_resource(
+            resource_arn=resource_arn,
+            new_tags=tags,
+        )
+        return json.dumps(dict())
+
+    def list_tags_for_resource(self) -> str:
+        resource_arn = self._get_param("ResourceArn")
+        tags = self.qldb_backend.list_tags_for_resource(
+            resource_arn=resource_arn,
+        )
+        return json.dumps(dict(Tags=tags))
diff --git a/moto/qldb/urls.py b/moto/qldb/urls.py
new file mode 100644
index 000000000..4919e9eee
--- /dev/null
+++ b/moto/qldb/urls.py
@@ -0,0 +1,14 @@
+"""qldb base URL and path."""
+
+from .responses import QLDBResponse
+
+url_bases = [
+    r"https?://qldb\.(.+)\.amazonaws\.com",
+]
+
+url_paths = {
+    "{0}/ledgers$": QLDBResponse.dispatch,
+    "{0}/ledgers/(?P<ledger_name>[^/]+)$": QLDBResponse.dispatch,
+    "{0}/tags/(?P<resource_arn>[^/]+)$": QLDBResponse.dispatch,
+    "{0}/tags/(?P<resource_arn>[^/]+)/mock_ledger$": QLDBResponse.dispatch,
+}
diff --git a/scripts/scaffold.py b/scripts/scaffold.py
index f0f2b43dd..e53354f9c 100755
--- a/scripts/scaffold.py
+++ b/scripts/scaffold.py
@@ -569,6 +569,8 @@ def insert_url(service, operation, api_protocol):  # pylint: disable=too-many-lo
         list(client._service_model._service_description["operations"].keys()),
     )
     uri = client._service_model.operation_model(aws_operation_name).http["requestUri"]
+    if "?" in uri:
+        uri = uri.split("?")[0]
 
     path = os.path.join(
         os.path.dirname(__file__), "..", "moto", get_escaped_service(service), "urls.py"
@@ -578,6 +580,7 @@ def insert_url(service, operation, api_protocol):  # pylint: disable=too-many-lo
 
     if any(_ for _ in lines if re.match(uri, _)):
         return
+    uri = BaseResponse.uri_to_regexp(uri)[1:-1]
 
     url_paths_found = False
     last_elem_line_index = -1
@@ -593,7 +596,7 @@ def insert_url(service, operation, api_protocol):  # pylint: disable=too-many-lo
 
     # generate url pattern
     if api_protocol == "rest-json":
-        new_line = f'    "{0}/.*$": {service_class}Response.dispatch,'
+        new_line = f'    "{{0}}{uri}$": {service_class}Response.dispatch,'
     elif api_protocol == "rest-xml":
         new_line = f'    "{{0}}{uri}$": {service_class}Response.{operation},'
     else:


Test patch:
diff --git a/tests/test_dynamodb/test_dynamodb_resource_policy.py b/tests/test_dynamodb/test_dynamodb_resource_policy.py
index 3be60fe57..16d447b27 100644
--- a/tests/test_dynamodb/test_dynamodb_resource_policy.py
+++ b/tests/test_dynamodb/test_dynamodb_resource_policy.py
@@ -293,6 +293,10 @@ def put_resource_policy(ddb, table_arn, action="dynamodb:*", revision_id=None):
         try:
             policy = ddb.get_resource_policy(ResourceArn=table_arn)
             assert policy["RevisionId"] == revision_id
+            if allow_aws_request():
+                # Policy is supposedly ready-  but may still complain that a ResourcePolicy update is in progress
+                # Hopefully AWS is ready afterwards
+                sleep(1)
             return revision_id
         except (ClientError, AssertionError):
             # Policy is not ready yet
diff --git a/tests/test_events/test_events.py b/tests/test_events/test_events.py
index aef843735..995cac188 100644
--- a/tests/test_events/test_events.py
+++ b/tests/test_events/test_events.py
@@ -2284,6 +2284,7 @@ def test_create_and_describe_connection():
     assert description["Description"] == "test description"
     assert description["AuthorizationType"] == "API_KEY"
     assert description["ConnectionState"] == "AUTHORIZED"
+    assert description["SecretArn"] is not None
     assert "CreationTime" in description
 
 
diff --git a/tests/test_qldb/__init__.py b/tests/test_qldb/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_qldb/test_qldb.py b/tests/test_qldb/test_qldb.py
new file mode 100644
index 000000000..54121d118
--- /dev/null
+++ b/tests/test_qldb/test_qldb.py
@@ -0,0 +1,116 @@
+"""Unit tests for qldb-supported APIs."""
+
+from datetime import datetime
+
+import boto3
+import pytest
+from botocore.exceptions import ClientError
+
+from moto import mock_aws
+
+# See our Development Tips on writing tests for hints on how to write good tests:
+# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
+
+
+@pytest.fixture(name="client")
+def fixture_qldb_client():
+    with mock_aws():
+        yield boto3.client("qldb", region_name="us-east-1")
+
+
+@mock_aws
+def test_create_describe_update_and_delete_ledger(client):
+    name = "mock_ledger"
+    permissions_mode = "STANDARD"
+    kms_key = "mock_key"
+    connection = client.create_ledger(
+        Name=name,
+        PermissionsMode=permissions_mode,
+        DeletionProtection=True,
+        KmsKey=kms_key,
+    )
+    assert connection["Name"] == name
+    assert connection["Arn"] == f"arn:aws:qldb:us-east-1:123456789012:ledger/{name}"
+    assert (
+        connection["KmsKeyArn"] == f"arn:aws:kms:us-east-1:123456789012:key/{kms_key}"
+    )
+    assert connection["PermissionsMode"] == permissions_mode
+    assert isinstance(connection["CreationDateTime"], datetime)
+    assert connection["DeletionProtection"]
+    assert connection["State"] == "ACTIVE"
+
+    connection = client.describe_ledger(Name=name)
+    assert connection["Name"] == name
+    assert connection["Arn"] == f"arn:aws:qldb:us-east-1:123456789012:ledger/{name}"
+    assert connection["PermissionsMode"] == permissions_mode
+    assert isinstance(connection["CreationDateTime"], datetime)
+    assert connection["DeletionProtection"]
+    assert connection["State"] == "ACTIVE"
+    assert (
+        connection["EncryptionDescription"]["KmsKeyArn"]
+        == f"arn:aws:kms:us-east-1:123456789012:key/{kms_key}"
+    )
+    assert connection["EncryptionDescription"]["EncryptionStatus"] == "ENABLED"
+
+    new_key = "new_mock_key"
+    connection = client.update_ledger(
+        Name=name, DeletionProtection=False, KmsKey=new_key
+    )
+    connection = client.describe_ledger(Name=name)
+    assert connection["Name"] == name
+    assert connection["Arn"] == f"arn:aws:qldb:us-east-1:123456789012:ledger/{name}"
+    assert connection["PermissionsMode"] == permissions_mode
+    assert isinstance(connection["CreationDateTime"], datetime)
+    assert not connection["DeletionProtection"]
+    assert connection["State"] == "ACTIVE"
+    assert (
+        connection["EncryptionDescription"]["KmsKeyArn"]
+        == f"arn:aws:kms:us-east-1:123456789012:key/{new_key}"
+    )
+    assert connection["EncryptionDescription"]["EncryptionStatus"] == "ENABLED"
+
+    connection = client.describe_ledger(Name=name)
+    assert connection["Name"] == name
+    assert connection["Arn"] == f"arn:aws:qldb:us-east-1:123456789012:ledger/{name}"
+    assert connection["PermissionsMode"] == permissions_mode
+    assert isinstance(connection["CreationDateTime"], datetime)
+    assert not connection["DeletionProtection"]
+    assert connection["State"] == "ACTIVE"
+    assert (
+        connection["EncryptionDescription"]["KmsKeyArn"]
+        == f"arn:aws:kms:us-east-1:123456789012:key/{new_key}"
+    )
+    assert connection["EncryptionDescription"]["EncryptionStatus"] == "ENABLED"
+
+    connection = client.delete_ledger(Name=name)
+    with pytest.raises(ClientError) as e:
+        client.describe_ledger(Name=name)
+    err = e.value.response["Error"]
+    assert err["Code"] == "LedgerNotFound"
+    assert err["Message"] == f"{name} does not match any existing ledger."
+
+
+@mock_aws
+def test_tag_resource_and_list_tags_for_resource(client):
+    name = "mock_ledger"
+    tags = {"key1": "value1", "key2": "value2"}
+    permissions_mode = "STANDARD"
+    kms_key = "mock_key"
+    connection = client.create_ledger(
+        Name=name,
+        Tags=tags,
+        PermissionsMode=permissions_mode,
+        DeletionProtection=True,
+        KmsKey=kms_key,
+    )
+    arn = connection["Arn"]
+
+    connection = client.list_tags_for_resource(ResourceArn=arn)
+    assert connection["Tags"]["key1"] == "value1"
+    assert connection["Tags"]["key2"] == "value2"
+    assert "key3" not in connection["Tags"]
+    client.tag_resource(ResourceArn=arn, Tags={"key3": "value3"})
+    connection = client.list_tags_for_resource(ResourceArn=arn)
+    assert connection["Tags"]["key1"] == "value1"
+    assert connection["Tags"]["key2"] == "value2"
+    assert connection["Tags"]["key3"] == "value3"
diff --git a/tests/test_stepfunctions/parser/__init__.py b/tests/test_stepfunctions/parser/__init__.py
index 11d3e17d9..46ac1a46d 100644
--- a/tests/test_stepfunctions/parser/__init__.py
+++ b/tests/test_stepfunctions/parser/__init__.py
@@ -1,5 +1,4 @@
 import json
-import os
 from functools import wraps
 from time import sleep
 from typing import TYPE_CHECKING, Callable, TypeVar
@@ -10,6 +9,7 @@ import requests
 
 from moto import mock_aws, settings
 from tests import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from tests import allow_aws_request
 from tests.test_stepfunctions.parser.templates.templates import load_template
 
 if TYPE_CHECKING:
@@ -31,11 +31,7 @@ def aws_verified(func):
 
     @wraps(func)
     def pagination_wrapper():
-        allow_aws_request = (
-            os.environ.get("MOTO_TEST_ALLOW_AWS_REQUEST", "false").lower() == "true"
-        )
-
-        if allow_aws_request:
+        if allow_aws_request():
             return func()
         else:
             with mock_aws():
@@ -74,7 +70,7 @@ def verify_execution_result(
     execution_arn, state_machine_arn = _start_execution(
         client, load_template(tmpl_name), exec_input, sfn_role
     )
-    for _ in range(10):
+    for _ in range(20):
         execution = client.describe_execution(executionArn=execution_arn)
         if expected_status is None or execution["status"] == expected_status:
             result = _verify_result(client, execution, execution_arn)
@@ -85,7 +81,7 @@ def verify_execution_result(
                 )
                 iam.delete_role(RoleName=role_name)
                 break
-        sleep(0.1)
+        sleep(1 if allow_aws_request() else 0.1)
     else:
         client.delete_state_machine(stateMachineArn=state_machine_arn)
         iam.delete_role_policy(RoleName=role_name, PolicyName="allowLambdaInvoke")