From cb932d6ee1b3b3571e4d4d9118635e2dbf58f0ef Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 9 Jan 2023 11:18:21 +0000 Subject: [PATCH] DEV: Apply syntax_tree formatting to `spec/*` --- .streerc | 1 - spec/fabricators/allowed_pm_users.rb | 4 +- spec/fabricators/api_key_fabricator.rb | 4 +- .../associated_group_fabricator.rb | 2 +- spec/fabricators/badge_fabricator.rb | 4 +- spec/fabricators/bookmark_fabricator.rb | 15 +- spec/fabricators/category_fabricator.rb | 11 +- spec/fabricators/color_scheme_fabricator.rb | 4 +- .../external_upload_stub_fabricator.rb | 25 +- spec/fabricators/group_fabricator.rb | 4 +- spec/fabricators/invite_fabricator.rb | 2 +- spec/fabricators/muted_user.rb | 4 +- spec/fabricators/notification_fabricator.rb | 12 +- spec/fabricators/permalink_fabricator.rb | 4 +- spec/fabricators/post_fabricator.rb | 53 +- spec/fabricators/post_revision_fabricator.rb | 4 +- spec/fabricators/reviewable_fabricator.rb | 51 +- spec/fabricators/screened_url_fabricator.rb | 4 +- .../sidebar_section_link_fabricator.rb | 8 +- .../single_sign_on_record_fabricator.rb | 4 +- spec/fabricators/tag_fabricator.rb | 4 +- spec/fabricators/tag_group_fabricator.rb | 4 +- .../topic_allowed_user_fabricator.rb | 4 +- spec/fabricators/topic_fabricator.rb | 22 +- spec/fabricators/upload_fabricator.rb | 27 +- spec/fabricators/user_api_key_fabricator.rb | 6 +- spec/fabricators/user_avatar_fabricator.rb | 4 +- spec/fabricators/user_fabricator.rb | 73 +- spec/fabricators/user_field_fabricator.rb | 2 +- spec/fabricators/user_history_fabricator.rb | 3 +- spec/fabricators/user_option_fabricator.rb | 3 +- spec/fabricators/user_profile_fabricator.rb | 4 +- .../user_second_factor_fabricator.rb | 2 +- .../user_security_key_fabricator.rb | 8 +- spec/fabricators/web_hook_fabricator.rb | 68 +- .../change/20990309014015_drop_email_logs.rb | 2 +- .../20990309014014_drop_post_columns.rb | 4 +- .../20990309014013_drop_email_logs_table.rb | 2 +- spec/fixtures/plugins/csp_extension/plugin.rb | 8 +- spec/fixtures/plugins/my_plugin/plugin.rb | 3 +- spec/helpers/application_helper_spec.rb | 403 +- spec/helpers/redis_snapshot_helper.rb | 8 +- spec/helpers/topics_helper_spec.rb | 5 +- .../helpers/user_notifications_helper_spec.rb | 91 +- spec/import_export/category_exporter_spec.rb | 16 +- .../category_structure_exporter_spec.rb | 12 +- spec/import_export/group_exporter_spec.rb | 10 +- spec/import_export/importer_spec.rb | 82 +- spec/import_export/topic_exporter_spec.rb | 12 +- .../track_setting_changes_spec.rb | 12 +- spec/integration/api_keys_spec.rb | 61 +- .../auto_reject_reviewable_users_spec.rb | 4 +- .../blocked_hotlinked_media_spec.rb | 68 +- spec/integration/category_tag_spec.rb | 693 ++- .../content_security_policy_spec.rb | 10 +- spec/integration/discord_omniauth_spec.rb | 96 +- spec/integration/email_style_spec.rb | 55 +- spec/integration/facebook_omniauth_spec.rb | 67 +- spec/integration/flags_spec.rb | 2 - spec/integration/github_omniauth_spec.rb | 162 +- spec/integration/group_spec.rb | 16 +- spec/integration/invalid_request_spec.rb | 23 +- .../invite_only_registration_spec.rb | 47 +- spec/integration/message_bus_spec.rb | 4 +- spec/integration/multisite_cookies_spec.rb | 12 +- spec/integration/multisite_spec.rb | 10 +- spec/integration/rate_limiting_spec.rb | 45 +- spec/integration/request_tracker_spec.rb | 16 +- spec/integration/same_ip_spammers_spec.rb | 54 +- spec/integration/spam_rules_spec.rb | 85 +- spec/integration/topic_auto_close_spec.rb | 39 +- spec/integration/topic_thumbnail_spec.rb | 49 +- spec/integration/twitter_omniauth_spec.rb | 70 +- spec/integration/watched_words_spec.rb | 231 +- spec/integrity/coding_style_spec.rb | 16 +- spec/integrity/common_mark_spec.rb | 116 +- spec/integrity/i18n_spec.rb | 20 +- spec/integrity/js_constants_spec.rb | 8 +- spec/integrity/oj_spec.rb | 4 +- spec/integrity/onceoff_integrity_spec.rb | 7 +- spec/integrity/site_setting_spec.rb | 14 +- spec/jobs/about_stats_spec.rb | 2 +- spec/jobs/activation_reminder_emails_spec.rb | 28 +- spec/jobs/auto_expire_user_api_keys_spec.rb | 8 +- spec/jobs/auto_queue_handler_spec.rb | 6 +- spec/jobs/automatic_group_membership_spec.rb | 27 +- .../bookmark_reminder_notifications_spec.rb | 14 +- spec/jobs/bulk_grant_trust_level_spec.rb | 9 +- spec/jobs/bulk_invite_spec.rb | 113 +- spec/jobs/bump_topic_spec.rb | 1 - spec/jobs/check_new_features_spec.rb | 91 +- spec/jobs/clean_dismissed_topic_users_spec.rb | 12 +- .../jobs/clean_up_associated_accounts_spec.rb | 23 +- spec/jobs/clean_up_email_logs_spec.rb | 10 +- spec/jobs/clean_up_inactive_users_spec.rb | 24 +- spec/jobs/clean_up_post_reply_keys_spec.rb | 12 +- .../jobs/clean_up_unused_staged_users_spec.rb | 2 +- spec/jobs/clean_up_uploads_spec.rb | 147 +- spec/jobs/clean_up_user_export_topics_spec.rb | 36 +- spec/jobs/close_topic_spec.rb | 61 +- .../correct_missing_dualstack_urls_spec.rb | 82 +- spec/jobs/crawl_topic_link_spec.rb | 1 - spec/jobs/create_linked_topic_spec.rb | 37 +- .../create_recent_post_search_indexes_spec.rb | 16 +- spec/jobs/create_user_reviewable_spec.rb | 12 +- spec/jobs/dashboard_stats_spec.rb | 18 +- spec/jobs/delete_replies_spec.rb | 25 +- spec/jobs/delete_topic_spec.rb | 10 +- spec/jobs/disable_bootstrap_mode_spec.rb | 15 +- spec/jobs/download_avatar_from_url_spec.rb | 9 +- spec/jobs/download_backup_email_spec.rb | 15 +- ...wnload_profile_background_from_url_spec.rb | 9 +- spec/jobs/emit_web_hook_event_spec.rb | 260 +- spec/jobs/enable_bootstrap_mode_spec.rb | 23 +- spec/jobs/enqueue_digest_emails_spec.rb | 109 +- spec/jobs/enqueue_suspect_users_spec.rb | 59 +- spec/jobs/export_csv_file_spec.rb | 201 +- spec/jobs/export_user_archive_spec.rb | 455 +- spec/jobs/feature_topic_users_spec.rb | 2 +- ...x_out_of_sync_user_uploaded_avatar_spec.rb | 14 +- ...ix_primary_emails_for_staged_users_spec.rb | 16 +- spec/jobs/fix_s3_etags_spec.rb | 4 +- ...r_usernames_and_groups_names_clash_spec.rb | 12 +- spec/jobs/grant_anniversary_badges_spec.rb | 4 +- ...grant_new_user_of_the_month_badges_spec.rb | 17 +- spec/jobs/heartbeat_spec.rb | 4 +- spec/jobs/ignored_users_summary_spec.rb | 19 +- spec/jobs/invalidate_inactive_admins_spec.rb | 33 +- spec/jobs/invite_email_spec.rb | 15 +- spec/jobs/jobs_base_spec.rb | 74 +- spec/jobs/jobs_spec.rb | 59 +- spec/jobs/mass_award_badge_spec.rb | 30 +- .../migrate_badge_image_to_uploads_spec.rb | 21 +- spec/jobs/notify_category_change_spec.rb | 12 +- .../notify_mailing_list_subscribers_spec.rb | 119 +- spec/jobs/notify_moved_posts_spec.rb | 30 +- spec/jobs/notify_reviewable_spec.rb | 84 +- spec/jobs/notify_tag_change_spec.rb | 93 +- spec/jobs/old_keys_reminder_spec.rb | 50 +- spec/jobs/open_topic_spec.rb | 50 +- .../pending_queued_posts_reminder_spec.rb | 36 +- .../jobs/pending_reviewables_reminder_spec.rb | 26 +- spec/jobs/pending_users_reminder_spec.rb | 13 +- spec/jobs/periodical_updates_spec.rb | 2 - spec/jobs/poll_mailbox_spec.rb | 44 +- .../post_update_topic_tracking_state_spec.rb | 4 +- spec/jobs/post_uploads_recovery_spec.rb | 10 +- spec/jobs/problem_checks_spec.rb | 18 +- spec/jobs/process_bulk_invite_emails_spec.rb | 4 +- spec/jobs/process_email_spec.rb | 2 - spec/jobs/process_post_spec.rb | 30 +- .../process_shelved_notifications_spec.rb | 24 +- spec/jobs/publish_topic_to_category_spec.rb | 46 +- spec/jobs/pull_hotlinked_images_spec.rb | 304 +- ...pull_user_profile_hotlinked_images_spec.rb | 24 +- spec/jobs/rebake_custom_emoji_posts_spec.rb | 12 +- .../refresh_users_reviewable_counts_spec.rb | 39 +- .../regular/bulk_user_title_update_spec.rb | 30 +- spec/jobs/regular/group_smtp_email_spec.rb | 120 +- .../publish_group_membership_updates_spec.rb | 63 +- .../update_post_uploads_secure_status_spec.rb | 2 +- spec/jobs/reindex_search_spec.rb | 40 +- spec/jobs/remove_banner_spec.rb | 34 +- spec/jobs/reviewable_priorities_spec.rb | 7 +- spec/jobs/send_system_message_spec.rb | 28 +- spec/jobs/suspicious_login_spec.rb | 7 +- spec/jobs/sync_acls_for_uploads_spec.rb | 10 +- spec/jobs/tl3_promotions_spec.rb | 17 +- spec/jobs/toggle_topic_closed_spec.rb | 92 +- spec/jobs/topic_timer_enqueuer_spec.rb | 32 +- spec/jobs/truncate_user_flag_stats_spec.rb | 5 +- spec/jobs/update_gravatar_spec.rb | 11 +- spec/jobs/update_s3_inventory_spec.rb | 15 +- spec/jobs/update_username_spec.rb | 19 +- spec/jobs/user_email_spec.rb | 513 ++- spec/lib/admin_confirmation_spec.rb | 23 +- spec/lib/admin_user_index_query_spec.rb | 68 +- spec/lib/archetype_spec.rb | 20 +- .../default_current_user_provider_spec.rb | 387 +- spec/lib/auth/discord_authenticator_spec.rb | 36 +- spec/lib/auth/facebook_authenticator_spec.rb | 63 +- spec/lib/auth/github_authenticator_spec.rb | 174 +- .../auth/google_oauth2_authenticator_spec.rb | 152 +- spec/lib/auth/managed_authenticator_spec.rb | 353 +- spec/lib/auth/result_spec.rb | 4 +- spec/lib/auth/twitter_authenticator_spec.rb | 22 +- .../backup_file_handler_multisite_spec.rb | 2 +- .../backup_file_handler_spec.rb | 18 +- spec/lib/backup_restore/backuper_spec.rb | 48 +- .../backup_restore/database_restorer_spec.rb | 56 +- .../backup_restore/local_backup_store_spec.rb | 57 +- .../backup_restore/meta_data_handler_spec.rb | 57 +- .../backup_restore/s3_backup_store_spec.rb | 141 +- .../shared_context_for_backup_restore.rb | 50 +- .../shared_examples_for_backup_store.rb | 70 +- .../backup_restore/system_interface_spec.rb | 52 +- .../backup_restore/uploads_restorer_spec.rb | 164 +- spec/lib/bookmark_manager_spec.rb | 189 +- spec/lib/bookmark_query_spec.rb | 98 +- ...mark_reminder_notification_handler_spec.rb | 22 +- spec/lib/browser_detection_spec.rb | 160 +- spec/lib/cache_spec.rb | 25 +- spec/lib/category_badge_spec.rb | 4 +- spec/lib/category_guardian_spec.rb | 22 +- .../common_passwords/common_passwords_spec.rb | 10 +- spec/lib/composer_messages_finder_spec.rb | 254 +- spec/lib/compression/engine_spec.rb | 58 +- spec/lib/concern/cached_counting_spec.rb | 18 +- spec/lib/concern/category_hashtag_spec.rb | 33 +- spec/lib/concern/has_custom_fields_spec.rb | 155 +- spec/lib/concern/has_search_data_spec.rb | 17 +- spec/lib/concern/positionable_spec.rb | 6 +- spec/lib/concern/searchable_spec.rb | 24 +- .../lib/concern/second_factor_manager_spec.rb | 219 +- spec/lib/content_buffer_spec.rb | 4 +- .../content_security_policy/builder_spec.rb | 39 +- spec/lib/content_security_policy_spec.rb | 412 +- spec/lib/cooked_post_processor_spec.rb | 769 ++-- spec/lib/crawler_detection_spec.rb | 193 +- spec/lib/current_user_spec.rb | 18 +- spec/lib/db_helper_spec.rb | 13 +- spec/lib/directory_helper_spec.rb | 24 +- spec/lib/discourse_diff_spec.rb | 105 +- spec/lib/discourse_event_spec.rb | 68 +- spec/lib/discourse_hub_spec.rb | 59 +- spec/lib/discourse_js_processor_spec.rb | 87 +- spec/lib/discourse_plugin_registry_spec.rb | 135 +- spec/lib/discourse_redis_spec.rb | 251 +- ...course_sourcemapping_url_processor_spec.rb | 12 +- spec/lib/discourse_spec.rb | 363 +- spec/lib/discourse_tagging_spec.rb | 604 +-- spec/lib/discourse_updates_spec.rb | 170 +- spec/lib/distributed_cache_spec.rb | 20 +- spec/lib/distributed_memoizer_spec.rb | 4 +- spec/lib/distributed_mutex_spec.rb | 70 +- spec/lib/email/authentication_results_spec.rb | 27 +- spec/lib/email/cleaner_spec.rb | 15 +- spec/lib/email/email_spec.rb | 83 +- spec/lib/email/message_builder_spec.rb | 226 +- spec/lib/email/processor_spec.rb | 104 +- spec/lib/email/receiver_spec.rb | 799 ++-- spec/lib/email/renderer_spec.rb | 17 +- spec/lib/email/sender_spec.rb | 448 +- spec/lib/email/styles_spec.rb | 207 +- spec/lib/email_cook_spec.rb | 20 +- spec/lib/email_updater_spec.rb | 223 +- spec/lib/encodings_spec.rb | 16 +- spec/lib/enum_spec.rb | 2 +- spec/lib/excerpt_parser_spec.rb | 48 +- spec/lib/feed_element_installer_spec.rb | 30 +- spec/lib/feed_item_accessor_spec.rb | 36 +- spec/lib/file_helper_spec.rb | 105 +- spec/lib/file_store/base_store_spec.rb | 107 +- spec/lib/file_store/local_store_spec.rb | 87 +- spec/lib/file_store/s3_store_spec.rb | 283 +- spec/lib/filter_best_posts_spec.rb | 39 +- spec/lib/final_destination/http_spec.rb | 9 +- spec/lib/final_destination/resolver_spec.rb | 4 +- spec/lib/final_destination_spec.rb | 365 +- spec/lib/flag_settings_spec.rb | 15 +- .../schema_migration_details_spec.rb | 1 - .../translate_accelerator_spec.rb | 200 +- spec/lib/gaps_spec.rb | 20 +- spec/lib/git_url_spec.rb | 4 +- spec/lib/global_path_spec.rb | 25 +- .../lib/group_email_credentials_check_spec.rb | 56 +- spec/lib/guardian/topic_guardian_spec.rb | 124 +- spec/lib/guardian/user_guardian_spec.rb | 118 +- spec/lib/guardian_spec.rb | 1321 +++--- spec/lib/has_errors_spec.rb | 19 +- spec/lib/highlight_js/highlight_js_spec.rb | 10 +- spec/lib/hijack_spec.rb | 87 +- spec/lib/html_prettify_spec.rb | 17 +- spec/lib/html_to_markdown_spec.rb | 151 +- spec/lib/i18n/discourse_i18n_spec.rb | 148 +- spec/lib/i18n/fallback_locale_list_spec.rb | 32 +- .../i18n_interpolation_keys_finder_spec.rb | 7 +- spec/lib/image_sizer_spec.rb | 48 +- spec/lib/imap/providers/detector_spec.rb | 4 +- spec/lib/imap/providers/generic_spec.rb | 94 +- spec/lib/imap/providers/gmail_spec.rb | 69 +- spec/lib/imap/sync_spec.rb | 609 ++- spec/lib/import/normalize_spec.rb | 3 +- spec/lib/inline_oneboxer_spec.rb | 246 +- spec/lib/js_locale_helper_spec.rb | 181 +- spec/lib/json_error_spec.rb | 4 +- spec/lib/letter_avatar_spec.rb | 2 +- spec/lib/message_id_service_spec.rb | 114 +- spec/lib/method_profiler_spec.rb | 16 +- spec/lib/middleware/anonymous_cache_spec.rb | 199 +- .../discourse_public_exceptions_spec.rb | 17 +- spec/lib/middleware/enforce_hostname_spec.rb | 20 +- spec/lib/middleware/request_tracker_spec.rb | 372 +- spec/lib/migration/column_dropper_spec.rb | 63 +- spec/lib/migration/safe_migrate_spec.rb | 53 +- spec/lib/migration/table_dropper_spec.rb | 28 +- .../lib/mini_sql_multisite_connection_spec.rb | 27 +- spec/lib/mobile_detection_spec.rb | 1 - spec/lib/new_post_manager_spec.rb | 469 ++- spec/lib/new_post_result_spec.rb | 4 +- spec/lib/onebox/domain_checker_spec.rb | 6 +- .../engine/allowlisted_generic_onebox_spec.rb | 215 +- spec/lib/onebox/engine/amazon_onebox_spec.rb | 157 +- .../engine/animated_image_onebox_spec.rb | 4 +- spec/lib/onebox/engine/audio_onebox_spec.rb | 35 +- .../lib/onebox/engine/cloudapp_onebox_spec.rb | 28 +- spec/lib/onebox/engine/gfycat_onebox_spec.rb | 4 +- .../engine/github_actions_onebox_spec.rb | 18 +- .../onebox/engine/github_blob_onebox_spec.rb | 22 +- .../engine/github_commit_onebox_spec.rb | 18 +- .../engine/github_folder_onebox_spec.rb | 33 +- .../onebox/engine/github_gist_onebox_spec.rb | 17 +- .../engine/github_pull_request_onebox_spec.rb | 29 +- .../onebox/engine/gitlab_blob_onebox_spec.rb | 9 +- .../onebox/engine/google_docs_onebox_spec.rb | 4 +- .../onebox/engine/google_drive_onebox_spec.rb | 12 +- .../onebox/engine/google_maps_onebox_spec.rb | 56 +- .../engine/google_photos_onebox_spec.rb | 16 +- .../engine/google_play_app_onebox_spec.rb | 6 +- spec/lib/onebox/engine/hackernews_spec.rb | 9 +- spec/lib/onebox/engine/html_spec.rb | 6 +- spec/lib/onebox/engine/image_onebox_spec.rb | 44 +- spec/lib/onebox/engine/imgur_onebox_spec.rb | 4 +- .../onebox/engine/instagram_onebox_spec.rb | 46 +- spec/lib/onebox/engine/json_spec.rb | 5 +- spec/lib/onebox/engine/kaltura_onebox_spec.rb | 50 +- spec/lib/onebox/engine/motoko_onebox_spec.rb | 13 +- spec/lib/onebox/engine/pdf_onebox_spec.rb | 8 +- spec/lib/onebox/engine/pubmed_onebox_spec.rb | 10 +- spec/lib/onebox/engine/reddit_media_onebox.rb | 4 +- spec/lib/onebox/engine/slides_onebox_spec.rb | 8 +- .../engine/stack_exchange_onebox_spec.rb | 86 +- spec/lib/onebox/engine/standard_embed_spec.rb | 51 +- spec/lib/onebox/engine/trello_onebox_spec.rb | 26 +- .../onebox/engine/twitch_clips_onebox_spec.rb | 6 +- .../engine/twitch_stream_onebox_spec.rb | 10 +- .../onebox/engine/twitch_video_onebox_spec.rb | 6 +- .../engine/twitter_status_onebox_spec.rb | 322 +- .../lib/onebox/engine/typeform_onebox_spec.rb | 27 +- spec/lib/onebox/engine/video_onebox_spec.rb | 33 +- .../onebox/engine/wikimedia_onebox_spec.rb | 12 +- .../onebox/engine/wikipedia_onebox_spec.rb | 16 +- spec/lib/onebox/engine/wistia_onebox_spec.rb | 19 +- spec/lib/onebox/engine/xkcd_spec.rb | 4 +- spec/lib/onebox/engine/youku_onebox_spec.rb | 22 +- spec/lib/onebox/engine/youtube_onebox_spec.rb | 149 +- spec/lib/onebox/engine_spec.rb | 24 +- spec/lib/onebox/helpers_spec.rb | 309 +- spec/lib/onebox/json_ld_spec.rb | 80 +- spec/lib/onebox/matcher_spec.rb | 19 +- spec/lib/onebox/movie_spec.rb | 47 +- spec/lib/onebox/open_graph_spec.rb | 14 +- spec/lib/onebox/preview_spec.rb | 26 +- spec/lib/onebox/status_check_spec.rb | 50 +- spec/lib/onebox_spec.rb | 14 +- spec/lib/oneboxer_spec.rb | 548 ++- spec/lib/onpdiff_spec.rb | 66 +- spec/lib/pbkdf2_spec.rb | 10 +- spec/lib/pinned_check_spec.rb | 8 +- spec/lib/plain_text_to_markdown_spec.rb | 104 +- spec/lib/plugin/filter_manager_spec.rb | 17 +- spec/lib/plugin/instance_spec.rb | 366 +- spec/lib/post_action_creator_spec.rb | 103 +- spec/lib/post_action_destroyer_spec.rb | 61 +- spec/lib/post_creator_spec.rb | 1257 +++--- spec/lib/post_destroyer_spec.rb | 325 +- spec/lib/post_jobs_enqueuer_spec.rb | 24 +- spec/lib/post_locker_spec.rb | 29 +- spec/lib/post_merger_spec.rb | 57 +- spec/lib/post_revisor_spec.rb | 772 ++-- spec/lib/presence_channel_spec.rb | 117 +- spec/lib/pretty_text/helpers_spec.rb | 30 +- spec/lib/pretty_text_spec.rb | 931 +++-- spec/lib/promotion_spec.rb | 55 +- spec/lib/quote_comparer_spec.rb | 3 +- spec/lib/rate_limiter/limit_exceeded_spec.rb | 14 +- spec/lib/rate_limiter_spec.rb | 70 +- spec/lib/retrieve_title_spec.rb | 159 +- spec/lib/rtl_spec.rb | 44 +- spec/lib/s3_cors_rulesets_spec.rb | 85 +- spec/lib/s3_helper_spec.rb | 166 +- spec/lib/s3_inventory_multisite_spec.rb | 5 +- spec/lib/s3_inventory_spec.rb | 152 +- spec/lib/scheduler/defer_spec.rb | 58 +- spec/lib/score_calculator_spec.rb | 8 +- spec/lib/scss_checker_spec.rb | 15 +- spec/lib/search_spec.rb | 1744 ++++---- .../discourse_connect_provider_spec.rb | 36 +- .../second_factor/actions/grant_admin_spec.rb | 56 +- spec/lib/second_factor/auth_manager_spec.rb | 110 +- spec/lib/seed_data/categories_spec.rb | 50 +- spec/lib/seed_data/topics_spec.rb | 26 +- spec/lib/shrink_uploaded_image_spec.rb | 88 +- .../lib/sidebar_section_links_updater_spec.rb | 72 +- spec/lib/site_icon_manager_spec.rb | 9 +- .../site_setting_extension_multisite_spec.rb | 20 +- spec/lib/site_setting_extension_spec.rb | 296 +- spec/lib/site_settings/db_provider_spec.rb | 1 - .../site_settings/defaults_provider_spec.rb | 117 +- .../local_process_provider_multisite_spec.rb | 4 +- .../local_process_provider_spec.rb | 1 - .../lib/site_settings/type_supervisor_spec.rb | 354 +- spec/lib/site_settings/validations_spec.rb | 263 +- spec/lib/site_settings/yaml_loader_spec.rb | 58 +- spec/lib/slug_spec.rb | 168 +- spec/lib/spam_handler_spec.rb | 8 +- spec/lib/stylesheet/compiler_spec.rb | 95 +- spec/lib/stylesheet/importer_spec.rb | 79 +- spec/lib/stylesheet/manager_spec.rb | 653 +-- spec/lib/suggested_topics_builder_spec.rb | 10 +- spec/lib/svg_sprite/svg_sprite_spec.rb | 85 +- spec/lib/system_message_spec.rb | 67 +- spec/lib/text_cleaner_spec.rb | 50 +- spec/lib/text_sentinel_spec.rb | 61 +- spec/lib/theme_flag_modifier_spec.rb | 4 +- spec/lib/theme_javascript_compiler_spec.rb | 115 +- spec/lib/theme_settings_manager_spec.rb | 6 +- spec/lib/theme_settings_parser_spec.rb | 28 +- spec/lib/theme_store/git_importer_spec.rb | 124 +- spec/lib/theme_store/zip_exporter_spec.rb | 137 +- spec/lib/theme_store/zip_importer_spec.rb | 21 +- spec/lib/timeline_lookup_spec.rb | 5 +- spec/lib/tiny_japanese_segmenter_spec.rb | 10 +- spec/lib/topic_creator_spec.rb | 503 ++- spec/lib/topic_publisher_spec.rb | 23 +- .../topic_query/private_message_lists_spec.rb | 236 +- spec/lib/topic_query_spec.rb | 985 +++-- spec/lib/topic_retriever_spec.rb | 29 +- .../lib/topic_upload_security_manager_spec.rb | 24 +- spec/lib/topic_view_spec.rb | 331 +- spec/lib/topics_bulk_action_spec.rb | 226 +- spec/lib/trust_level_spec.rb | 6 +- spec/lib/twitter_api_spec.rb | 8 +- spec/lib/unread_spec.rb | 33 +- spec/lib/upload_creator_spec.rb | 404 +- spec/lib/upload_markdown_spec.rb | 9 +- spec/lib/upload_recovery_spec.rb | 230 +- spec/lib/upload_security_spec.rb | 81 +- spec/lib/url_helper_spec.rb | 55 +- spec/lib/user_comm_screener_spec.rb | 107 +- spec/lib/user_lookup_spec.rb | 28 +- spec/lib/user_name_suggester_spec.rb | 125 +- .../allowed_ip_address_validator_spec.rb | 14 +- ..._search_priority_weights_validator_spec.rb | 12 +- .../censored_words_validator_spec.rb | 16 +- .../validators/css_color_validator_spec.rb | 20 +- .../delete_rejected_email_after_days_spec.rb | 12 +- .../email_address_validator_spec.rb | 23 +- .../email_setting_validator_spec.rb | 8 +- spec/lib/validators/email_validator_spec.rb | 30 +- .../enable_invite_only_validator_spec.rb | 15 +- ...e_local_logins_via_email_validator_spec.rb | 40 +- .../validators/enable_sso_validator_spec.rb | 76 +- .../group_setting_validator_spec.rb | 8 +- .../host_list_setting_validator_spec.rb | 6 +- .../integer_setting_validator_spec.rb | 31 +- .../ip_address_format_validator_spec.rb | 11 +- .../validators/max_emojis_validator_spec.rb | 25 +- .../max_username_length_validator_spec.rb | 25 +- .../min_username_length_validator_spec.rb | 23 +- .../lib/validators/password_validator_spec.rb | 16 +- spec/lib/validators/post_validator_spec.rb | 45 +- .../quality_title_validator_spec.rb | 32 +- .../regex_presence_validator_spec.rb | 12 +- .../regex_setting_validator_spec.rb | 8 +- .../validators/regexp_list_validator_spec.rb | 6 +- .../reply_by_email_address_validator_spec.rb | 19 +- .../reply_by_email_enabled_validator_spec.rb | 5 +- .../search_tokenize_chinese_validator_spec.rb | 6 +- ...search_tokenize_japanese_validator_spec.rb | 6 +- .../selectable_avatars_mode_validator_spec.rb | 2 +- .../sso_overrides_email_validator_spec.rb | 30 +- .../string_setting_validator_spec.rb | 68 +- .../lib/validators/timezone_validator_spec.rb | 4 +- .../topic_title_length_validator_spec.rb | 32 +- ...icode_username_allowlist_validator_spec.rb | 3 +- spec/lib/validators/upload_validator_spec.rb | 86 +- spec/lib/validators/url_validator_spec.rb | 12 +- .../username_setting_validator_spec.rb | 28 +- spec/lib/version_spec.rb | 77 +- spec/lib/webauthn/challenge_generator_spec.rb | 8 +- ...ecurity_key_authentication_service_spec.rb | 151 +- .../security_key_registration_service_spec.rb | 110 +- spec/lib/wizard/step_updater_spec.rb | 341 +- spec/lib/wizard/wizard_builder_spec.rb | 70 +- spec/lib/wizard/wizard_spec.rb | 119 +- spec/lib/wizard/wizard_step_spec.rb | 18 +- spec/mailers/group_smtp_mailer_spec.rb | 97 +- spec/mailers/invite_mailer_spec.rb | 96 +- spec/mailers/rejection_mailer_spec.rb | 22 +- spec/mailers/subscription_mailer_spec.rb | 4 +- spec/mailers/test_mailer_spec.rb | 8 +- spec/mailers/user_notifications_spec.rb | 806 ++-- spec/mailers/version_mailer_spec.rb | 10 +- spec/models/about_spec.rb | 65 +- spec/models/admin_dashboard_data_spec.rb | 183 +- spec/models/api_key_scope_spec.rb | 9 +- spec/models/api_key_spec.rb | 44 +- spec/models/application_request_spec.rb | 6 +- spec/models/associated_group_spec.rb | 2 +- spec/models/badge_spec.rb | 115 +- spec/models/badge_type_spec.rb | 1 - spec/models/bookmark_spec.rb | 53 +- spec/models/category_featured_topic_spec.rb | 55 +- spec/models/category_group_spec.rb | 6 +- spec/models/category_list_spec.rb | 135 +- spec/models/category_spec.rb | 426 +- spec/models/category_user_spec.rb | 249 +- spec/models/child_theme_spec.rb | 8 +- spec/models/color_scheme_color_spec.rb | 21 +- spec/models/color_scheme_spec.rb | 103 +- spec/models/digest_email_site_setting_spec.rb | 12 +- spec/models/directory_item_spec.rb | 112 +- spec/models/discourse_connect_spec.rb | 274 +- spec/models/do_not_disturb_timing_spec.rb | 2 +- spec/models/draft_sequence_spec.rb | 38 +- spec/models/draft_spec.rb | 151 +- spec/models/email_change_request_spec.rb | 11 +- spec/models/email_log_spec.rb | 87 +- spec/models/email_token_spec.rb | 102 +- spec/models/embeddable_host_spec.rb | 112 +- spec/models/emoji_spec.rb | 77 +- spec/models/given_daily_like_spec.rb | 9 +- spec/models/global_setting_spec.rb | 55 +- spec/models/group_archived_message_spec.rb | 42 +- spec/models/group_history_spec.rb | 89 +- spec/models/group_spec.rb | 441 +- spec/models/group_user_spec.rb | 111 +- spec/models/incoming_link_spec.rb | 83 +- spec/models/incoming_links_report_spec.rb | 222 +- spec/models/invite_redeemer_spec.rb | 368 +- spec/models/invite_spec.rb | 345 +- spec/models/javascript_cache_spec.rb | 25 +- spec/models/locale_site_setting_spec.rb | 91 +- .../mailing_list_mode_site_setting_spec.rb | 14 +- spec/models/notification_spec.rb | 574 ++- spec/models/optimized_image_spec.rb | 113 +- spec/models/permalink_spec.rb | 22 +- spec/models/plugin_store_spec.rb | 13 +- spec/models/post_action_spec.rb | 336 +- spec/models/post_action_type_spec.rb | 16 +- spec/models/post_analyzer_spec.rb | 129 +- spec/models/post_mover_spec.rb | 967 +++-- spec/models/post_reply_key_spec.rb | 10 +- spec/models/post_reply_spec.rb | 2 +- spec/models/post_revision_spec.rb | 12 +- spec/models/post_spec.rb | 718 ++-- spec/models/post_timing_spec.rb | 87 +- ...ivate_message_topic_tracking_state_spec.rb | 188 +- spec/models/published_page_spec.rb | 4 +- spec/models/quoted_post_spec.rb | 19 +- spec/models/remote_theme_spec.rb | 63 +- spec/models/report_spec.rb | 723 ++-- spec/models/reviewable_claimed_topic_spec.rb | 2 - spec/models/reviewable_flagged_post_spec.rb | 59 +- spec/models/reviewable_history_spec.rb | 2 - spec/models/reviewable_post_spec.rb | 50 +- spec/models/reviewable_queued_post_spec.rb | 84 +- spec/models/reviewable_score_spec.rb | 23 +- spec/models/reviewable_spec.rb | 169 +- spec/models/reviewable_user_spec.rb | 68 +- spec/models/s3_region_site_setting_spec.rb | 12 +- spec/models/screened_email_spec.rb | 34 +- spec/models/screened_ip_address_spec.rb | 375 +- spec/models/screened_url_spec.rb | 98 +- spec/models/search_log_spec.rb | 182 +- spec/models/sidebar_section_link_spec.rb | 33 +- spec/models/site_setting_spec.rb | 112 +- spec/models/site_spec.rb | 55 +- spec/models/sitemap_spec.rb | 69 +- spec/models/skipped_email_log_spec.rb | 82 +- spec/models/stylesheet_cache_spec.rb | 3 +- spec/models/tag_group_spec.rb | 98 +- spec/models/tag_spec.rb | 85 +- spec/models/tag_user_spec.rb | 167 +- spec/models/theme_field_spec.rb | 408 +- spec/models/theme_modifier_set_spec.rb | 8 +- spec/models/theme_spec.rb | 584 ++- spec/models/top_menu_item_spec.rb | 11 +- spec/models/top_topic_spec.rb | 54 +- spec/models/topic_converter_spec.rb | 91 +- spec/models/topic_embed_spec.rb | 256 +- spec/models/topic_featured_users_spec.rb | 15 +- spec/models/topic_group_spec.rb | 46 +- spec/models/topic_invite_spec.rb | 2 - spec/models/topic_link_click_spec.rb | 255 +- spec/models/topic_link_spec.rb | 288 +- spec/models/topic_list_spec.rb | 73 +- .../models/topic_participants_summary_spec.rb | 9 +- spec/models/topic_posters_summary_spec.rb | 51 +- spec/models/topic_spec.rb | 1723 ++++---- spec/models/topic_tag_spec.rb | 26 +- spec/models/topic_thumbnail_spec.rb | 2 +- spec/models/topic_timer_spec.rb | 165 +- spec/models/topic_tracking_state_spec.rb | 382 +- spec/models/topic_user_spec.rb | 338 +- spec/models/topic_view_item_spec.rb | 6 +- spec/models/translation_override_spec.rb | 228 +- spec/models/trust_level3_requirements_spec.rb | 149 +- .../trust_level_and_staff_setting_spec.rb | 8 +- spec/models/trust_level_setting_spec.rb | 8 +- spec/models/unsubscribe_key_spec.rb | 22 +- spec/models/upload_reference_spec.rb | 199 +- spec/models/upload_spec.rb | 275 +- spec/models/user_action_spec.rb | 144 +- spec/models/user_api_key_spec.rb | 26 +- spec/models/user_archived_message_spec.rb | 21 +- spec/models/user_auth_token_spec.rb | 165 +- spec/models/user_avatar_spec.rb | 98 +- spec/models/user_badge_spec.rb | 72 +- spec/models/user_bookmark_list_spec.rb | 4 +- spec/models/user_email_spec.rb | 12 +- spec/models/user_export_spec.rb | 38 +- spec/models/user_field_spec.rb | 8 +- spec/models/user_history_spec.rb | 51 +- .../models/user_notification_schedule_spec.rb | 62 +- spec/models/user_option_spec.rb | 49 +- spec/models/user_profile_spec.rb | 145 +- spec/models/user_profile_view_spec.rb | 12 +- spec/models/user_search_spec.rb | 56 +- spec/models/user_second_factor_spec.rb | 4 +- spec/models/user_spec.rb | 1696 ++++---- spec/models/user_stat_spec.rb | 118 +- spec/models/user_status_spec.rb | 2 +- spec/models/user_summary_spec.rb | 1 - spec/models/user_visit_spec.rb | 10 +- spec/models/username_validator_spec.rb | 193 +- spec/models/watched_word_spec.rb | 66 +- spec/models/web_crawler_request_spec.rb | 9 +- spec/models/web_hook_event_spec.rb | 23 +- spec/models/web_hook_spec.rb | 247 +- spec/multisite/distributed_cache_spec.rb | 27 +- spec/multisite/jobs_spec.rb | 10 +- spec/multisite/pausable_multisite_spec.rb | 22 +- spec/multisite/pausable_spec.rb | 8 +- spec/multisite/post_spec.rb | 15 +- spec/multisite/request_tracker_spec.rb | 103 +- spec/multisite/s3_store_spec.rb | 171 +- spec/multisite/site_settings_spec.rb | 38 +- spec/rails_helper.rb | 196 +- spec/requests/about_controller_spec.rb | 10 +- spec/requests/admin/admin_controller_spec.rb | 18 +- spec/requests/admin/api_controller_spec.rb | 173 +- .../requests/admin/backups_controller_spec.rb | 564 +-- spec/requests/admin/badges_controller_spec.rb | 315 +- .../admin/color_schemes_controller_spec.rb | 46 +- .../admin/dashboard_controller_spec.rb | 126 +- spec/requests/admin/email_controller_spec.rb | 287 +- .../admin/email_styles_controller_spec.rb | 57 +- .../admin/email_templates_controller_spec.rb | 289 +- .../admin/embeddable_hosts_controller_spec.rb | 59 +- .../admin/embedding_controller_spec.rb | 30 +- spec/requests/admin/emojis_controller_spec.rb | 129 +- spec/requests/admin/groups_controller_spec.rb | 409 +- .../admin/impersonate_controller_spec.rb | 14 +- .../admin/permalinks_controller_spec.rb | 99 +- .../requests/admin/plugins_controller_spec.rb | 6 +- .../requests/admin/reports_controller_spec.rb | 122 +- .../admin/robots_txt_controller_spec.rb | 6 +- .../admin/screened_emails_controller_spec.rb | 12 +- .../screened_ip_addresses_controller_spec.rb | 23 +- .../admin/screened_urls_controller_spec.rb | 4 +- spec/requests/admin/search_logs_spec.rb | 26 +- .../admin/site_settings_controller_spec.rb | 436 +- .../admin/site_texts_controller_spec.rb | 678 +-- .../staff_action_logs_controller_spec.rb | 60 +- spec/requests/admin/themes_controller_spec.rb | 603 +-- .../admin/user_fields_controller_spec.rb | 161 +- spec/requests/admin/users_controller_spec.rb | 827 ++-- .../admin/versions_controller_spec.rb | 12 +- .../admin/watched_words_controller_spec.rb | 161 +- .../admin/web_hooks_controller_spec.rb | 210 +- spec/requests/api/backups_spec.rb | 76 +- spec/requests/api/badges_spec.rb | 79 +- spec/requests/api/categories_spec.rb | 103 +- spec/requests/api/groups_spec.rb | 158 +- spec/requests/api/invites_spec.rb | 148 +- spec/requests/api/notifications_spec.rb | 153 +- spec/requests/api/posts_spec.rb | 1142 +++-- spec/requests/api/private_messages_spec.rb | 577 ++- spec/requests/api/schemas/schema_loader.rb | 5 +- spec/requests/api/search_spec.rb | 28 +- spec/requests/api/shared/shared_examples.rb | 6 +- spec/requests/api/site_spec.rb | 24 +- spec/requests/api/tags_spec.rb | 653 +-- spec/requests/api/topics_spec.rb | 1330 +++--- spec/requests/api/uploads_spec.rb | 228 +- spec/requests/api/user_badges_spec.rb | 22 +- spec/requests/api/users_spec.rb | 1256 +++--- spec/requests/application_controller_spec.rb | 542 ++- .../associate_accounts_controller_spec.rb | 25 +- spec/requests/badges_controller_spec.rb | 46 +- spec/requests/bookmarks_controller_spec.rb | 101 +- spec/requests/bootstrap_controller_spec.rb | 63 +- spec/requests/categories_controller_spec.rb | 621 +-- spec/requests/clicks_controller_spec.rb | 11 +- spec/requests/composer_controller_spec.rb | 353 +- .../composer_messages_controller_spec.rb | 47 +- spec/requests/csp_reports_controller_spec.rb | 63 +- .../directory_columns_controller_spec.rb | 30 +- .../directory_items_controller_spec.rb | 134 +- .../do_not_disturb_controller_spec.rb | 34 +- spec/requests/drafts_controller_spec.rb | 172 +- spec/requests/email_controller_spec.rb | 173 +- spec/requests/embed_controller_spec.rb | 213 +- spec/requests/exceptions_controller_spec.rb | 18 +- spec/requests/export_csv_controller_spec.rb | 18 +- .../requests/extra_locales_controller_spec.rb | 121 +- .../finish_installation_controller_spec.rb | 106 +- spec/requests/forums_controller_spec.rb | 8 +- spec/requests/groups_controller_spec.rb | 1308 +++--- spec/requests/hashtags_controller_spec.rb | 82 +- .../requests/inline_onebox_controller_spec.rb | 10 +- spec/requests/invites_controller_spec.rb | 994 +++-- spec/requests/list_controller_spec.rb | 354 +- spec/requests/metadata_controller_spec.rb | 99 +- .../requests/notifications_controller_spec.rb | 278 +- .../omniauth_callbacks_controller_spec.rb | 454 +- spec/requests/onebox_controller_spec.rb | 71 +- spec/requests/permalinks_controller_spec.rb | 10 +- .../post_action_users_controller_spec.rb | 84 +- spec/requests/post_actions_controller_spec.rb | 220 +- spec/requests/post_readers_controller_spec.rb | 68 +- spec/requests/posts_controller_spec.rb | 1388 +++--- spec/requests/presence_controller_spec.rb | 130 +- .../published_pages_controller_spec.rb | 118 +- .../push_notification_controller_spec.rb | 110 +- spec/requests/qunit_controller_spec.rb | 24 +- ...viewable_claimed_topics_controller_spec.rb | 89 +- spec/requests/reviewables_controller_spec.rb | 396 +- spec/requests/robots_txt_controller_spec.rb | 122 +- spec/requests/safe_mode_controller_spec.rb | 25 +- spec/requests/search_controller_spec.rb | 508 ++- spec/requests/session_controller_spec.rb | 1841 ++++---- .../similar_topics_controller_spec.rb | 22 +- spec/requests/site_controller_spec.rb | 12 +- spec/requests/sitemap_controller_spec.rb | 98 +- spec/requests/static_controller_spec.rb | 217 +- spec/requests/steps_controller_spec.rb | 41 +- spec/requests/stylesheets_controller_spec.rb | 20 +- spec/requests/svg_sprite_controller_spec.rb | 44 +- spec/requests/tag_groups_controller_spec.rb | 45 +- spec/requests/tags_controller_spec.rb | 586 +-- .../theme_javascripts_controller_spec.rb | 69 +- spec/requests/topics_controller_spec.rb | 2691 ++++++------ .../uploads_controller_multisite_spec.rb | 2 +- spec/requests/uploads_controller_spec.rb | 933 +++-- spec/requests/user_actions_controller_spec.rb | 41 +- .../requests/user_api_keys_controller_spec.rb | 52 +- spec/requests/user_avatars_controller_spec.rb | 114 +- spec/requests/user_badges_controller_spec.rb | 264 +- spec/requests/user_status_controller_spec.rb | 66 +- spec/requests/users_controller_spec.rb | 3708 +++++++++-------- spec/requests/users_email_controller_spec.rb | 316 +- spec/requests/webhooks_controller_spec.rb | 188 +- spec/requests/wizard_controller_spec.rb | 12 +- spec/script/import_scripts/base_spec.rb | 16 +- .../support/bbcode/xml_to_markdown_spec.rb | 164 +- .../vanilla_body_parser_spec.rb | 206 +- .../admin_plugin_serializer_spec.rb | 8 +- .../admin_user_list_serializer_spec.rb | 14 +- .../basic_group_serializer_spec.rb | 60 +- .../basic_group_user_serializer_spec.rb | 26 +- .../serializers/basic_post_serializer_spec.rb | 1 - ...reviewable_flagged_post_serializer_spec.rb | 6 +- ..._reviewable_queued_post_serializer_spec.rb | 5 +- .../basic_reviewable_user_serializer_spec.rb | 4 +- .../serializers/basic_user_serializer_spec.rb | 8 +- .../category_detailed_serializer_spec.rb | 11 +- spec/serializers/category_serializer_spec.rb | 35 +- .../category_upload_serializer_spec.rb | 4 +- .../current_user_serializer_spec.rb | 95 +- .../detailed_user_badge_serializer_spec.rb | 76 +- .../directory_item_serializer_spec.rb | 7 +- spec/serializers/emoji_serializer_spec.rb | 26 +- .../serializers/found_user_serializer_spec.rb | 2 +- .../serializers/group_show_serializer_spec.rb | 60 +- .../serializers/group_user_serializer_spec.rb | 2 +- .../listable_topic_serializer_spec.rb | 25 +- .../new_post_result_serializer_spec.rb | 7 +- .../notification_serializer_spec.rb | 6 +- .../pending_post_serializer_spec.rb | 4 +- .../post_revision_serializer_spec.rb | 61 +- spec/serializers/post_serializer_spec.rb | 100 +- ...reviewable_flagged_post_serializer_spec.rb | 8 +- .../reviewable_queued_post_serializer_spec.rb | 42 +- .../reviewable_score_serializer_spec.rb | 55 +- .../serializers/reviewable_serializer_spec.rb | 9 +- .../reviewable_user_serializer_spec.rb | 8 +- .../single_sign_on_record_serializer_spec.rb | 11 +- spec/serializers/site_serializer_spec.rb | 130 +- .../suggested_topic_serializer_spec.rb | 42 +- spec/serializers/tag_group_serializer_spec.rb | 9 +- spec/serializers/theme_serializer_spec.rb | 9 +- .../serializers/topic_link_serializer_spec.rb | 3 +- .../topic_list_item_serializer_spec.rb | 69 +- .../serializers/topic_list_serializer_spec.rb | 12 +- .../topic_tracking_state_serializer_spec.rb | 4 +- .../topic_view_details_serializer_spec.rb | 32 +- .../topic_view_posts_serializer_spec.rb | 40 +- .../serializers/topic_view_serializer_spec.rb | 274 +- spec/serializers/upload_serializer_spec.rb | 14 +- .../user_auth_token_serializer_spec.rb | 23 +- .../serializers/user_badge_serializer_spec.rb | 66 +- .../user_bookmark_list_serializer_spec.rb | 5 +- spec/serializers/user_card_serializer_spec.rb | 1 - .../user_post_bookmark_serializer_spec.rb | 6 +- spec/serializers/user_serializer_spec.rb | 240 +- .../user_summary_serializer_spec.rb | 11 +- .../web_hook_post_serializer_spec.rb | 6 +- .../web_hook_topic_view_serializer_spec.rb | 17 +- .../web_hook_user_serializer_spec.rb | 8 +- spec/serializers/wizard_serializer_spec.rb | 41 +- .../services/anonymous_shadow_creator_spec.rb | 1 - spec/services/auto_silence_spec.rb | 76 +- spec/services/badge_granter_spec.rb | 270 +- spec/services/base_bookmarkable_spec.rb | 33 +- .../category_hashtag_data_source_spec.rb | 5 +- spec/services/color_scheme_revisor_spec.rb | 42 +- spec/services/destroy_task_spec.rb | 51 +- .../email_settings_exception_handler_spec.rb | 38 +- .../services/email_settings_validator_spec.rb | 199 +- spec/services/email_style_updater_spec.rb | 50 +- spec/services/external_upload_manager_spec.rb | 74 +- spec/services/flag_sockpuppets_spec.rb | 206 +- spec/services/group_action_logger_spec.rb | 50 +- spec/services/group_mentions_updater_spec.rb | 39 +- spec/services/group_message_spec.rb | 122 +- .../hashtag_autocomplete_service_spec.rb | 4 +- spec/services/heat_settings_updater_spec.rb | 44 +- .../services/inline_uploads_multisite_spec.rb | 4 +- spec/services/inline_uploads_spec.rb | 29 +- spec/services/notification_emailer_spec.rb | 98 +- .../consolidate_notifications_spec.rb | 29 +- .../consolidation_planner_spec.rb | 35 +- spec/services/post_action_notifier_spec.rb | 211 +- spec/services/post_alerter_spec.rb | 1469 ++++--- spec/services/post_bookmarkable_spec.rb | 54 +- spec/services/post_owner_changer_spec.rb | 174 +- .../services/push_notification_pusher_spec.rb | 86 +- spec/services/random_topic_selector_spec.rb | 11 +- spec/services/search_indexer_spec.rb | 178 +- .../sidebar_site_settings_backfiller_spec.rb | 317 +- spec/services/site_settings_spec.rb | 24 +- spec/services/staff_action_logger_spec.rb | 340 +- spec/services/tag_hashtag_data_source_spec.rb | 2 +- spec/services/themes_spec.rb | 98 +- spec/services/topic_bookmarkable_spec.rb | 54 +- spec/services/topic_status_updater_spec.rb | 69 +- spec/services/topic_timestamp_changer_spec.rb | 19 +- spec/services/trust_level_granter_spec.rb | 6 +- spec/services/user_activator_spec.rb | 4 +- spec/services/user_anonymizer_spec.rb | 124 +- spec/services/user_authenticator_spec.rb | 16 +- spec/services/user_destroyer_spec.rb | 160 +- spec/services/user_merger_spec.rb | 406 +- ...er_notification_schedule_processor_spec.rb | 100 +- spec/services/user_silencer_spec.rb | 52 +- spec/services/user_stat_count_updater_spec.rb | 8 +- spec/services/user_updater_spec.rb | 339 +- spec/services/username_changer_spec.rb | 411 +- .../services/username_checker_service_spec.rb | 44 +- spec/services/wildcard_domain_checker_spec.rb | 40 +- spec/services/wildcard_url_checker_spec.rb | 59 +- spec/services/word_watcher_spec.rb | 262 +- spec/support/bookmarkable_helper.rb | 9 +- .../common_basic_reviewable_serializer.rb | 4 +- spec/support/concurrency.rb | 33 +- spec/support/diagnostics_helper.rb | 1 - spec/support/discourse_event_helper.rb | 11 +- spec/support/fake_s3.rb | 141 +- spec/support/helpers.rb | 42 +- spec/support/imap_helper.rb | 13 +- spec/support/integration_helpers.rb | 26 +- spec/support/match_html_matcher.rb | 19 +- spec/support/onebox_helpers.rb | 7 +- spec/support/rate_limit_matcher.rb | 4 +- .../session_controller_helper_routes.rb | 13 +- .../shared_examples_for_stats_cacheable.rb | 20 +- spec/support/sidekiq_helpers.rb | 27 +- spec/support/system_helpers.rb | 9 +- spec/support/test_second_factor_action.rb | 4 +- spec/support/time_matcher.rb | 24 +- ...opic_guardian_can_see_consistency_check.rb | 3 +- spec/support/uploads_helpers.rb | 10 +- .../user_sidebar_serializer_attributes.rb | 65 +- .../shared_examples_for_versioned_model.rb | 2 +- spec/support/webauthn_integration_helpers.rb | 37 +- spec/swagger_helper.rb | 42 +- spec/system/admin_customize_themes_spec.rb | 12 +- .../page_objects/pages/admin_settings.rb | 6 +- spec/system/page_objects/pages/category.rb | 6 +- spec/system/page_objects/pages/search.rb | 6 +- spec/system/page_objects/pages/user.rb | 4 +- .../page_objects/pages/user_preferences.rb | 4 +- .../pages/user_preferences_sidebar.rb | 9 +- spec/system/search_spec.rb | 6 +- spec/system/user_page/staff_info_spec.rb | 12 +- .../user_preferences_navigation_spec.rb | 4 +- .../viewing_sidebar_preferences_spec.rb | 39 +- spec/tasks/incoming_emails_spec.rb | 16 +- spec/tasks/posts_spec.rb | 62 +- spec/tasks/redis_spec.rb | 37 +- spec/tasks/uploads_spec.rb | 47 +- spec/views/list/list.erb_spec.rb | 16 +- .../failure.html.erb_spec.rb | 8 +- spec/views/topics/show.html.erb_spec.rb | 16 +- 907 files changed, 58693 insertions(+), 45909 deletions(-) diff --git a/.streerc b/.streerc index 615e88d6e24..016c131272c 100644 --- a/.streerc +++ b/.streerc @@ -3,4 +3,3 @@ --ignore-files=app/* --ignore-files=db/* --ignore-files=lib/* ---ignore-files=spec/* diff --git a/spec/fabricators/allowed_pm_users.rb b/spec/fabricators/allowed_pm_users.rb index c160aef83dd..fd7286396df 100644 --- a/spec/fabricators/allowed_pm_users.rb +++ b/spec/fabricators/allowed_pm_users.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:allowed_pm_user) do - user -end +Fabricator(:allowed_pm_user) { user } diff --git a/spec/fabricators/api_key_fabricator.rb b/spec/fabricators/api_key_fabricator.rb index 2f75d78c0bb..b2c6748fb13 100644 --- a/spec/fabricators/api_key_fabricator.rb +++ b/spec/fabricators/api_key_fabricator.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:api_key) do - -end +Fabricator(:api_key) {} diff --git a/spec/fabricators/associated_group_fabricator.rb b/spec/fabricators/associated_group_fabricator.rb index f7aff76893c..15117bae957 100644 --- a/spec/fabricators/associated_group_fabricator.rb +++ b/spec/fabricators/associated_group_fabricator.rb @@ -2,6 +2,6 @@ Fabricator(:associated_group) do name { sequence(:name) { |n| "group_#{n}" } } - provider_name 'google' + provider_name "google" provider_id { SecureRandom.hex(20) } end diff --git a/spec/fabricators/badge_fabricator.rb b/spec/fabricators/badge_fabricator.rb index b99fe209e7a..7ec3cb1b3b5 100644 --- a/spec/fabricators/badge_fabricator.rb +++ b/spec/fabricators/badge_fabricator.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -Fabricator(:badge_type) do - name { sequence(:name) { |i| "Silver #{i}" } } -end +Fabricator(:badge_type) { name { sequence(:name) { |i| "Silver #{i}" } } } Fabricator(:badge) do name { sequence(:name) { |i| "Badge #{i}" } } diff --git a/spec/fabricators/bookmark_fabricator.rb b/spec/fabricators/bookmark_fabricator.rb index 30dafdf0f11..ba9271a22e2 100644 --- a/spec/fabricators/bookmark_fabricator.rb +++ b/spec/fabricators/bookmark_fabricator.rb @@ -10,13 +10,14 @@ end Fabricator(:bookmark_next_business_day_reminder, from: :bookmark) do reminder_at do - date = if Time.zone.now.friday? - Time.zone.now + 3.days - elsif Time.zone.now.saturday? - Time.zone.now + 2.days - else - Time.zone.now + 1.day - end + date = + if Time.zone.now.friday? + Time.zone.now + 3.days + elsif Time.zone.now.saturday? + Time.zone.now + 2.days + else + Time.zone.now + 1.day + end date.iso8601 end reminder_set_at { Time.zone.now } diff --git a/spec/fabricators/category_fabricator.rb b/spec/fabricators/category_fabricator.rb index 0e14967af41..eb6dbcbd0cf 100644 --- a/spec/fabricators/category_fabricator.rb +++ b/spec/fabricators/category_fabricator.rb @@ -6,9 +6,7 @@ Fabricator(:category) do user end -Fabricator(:category_with_definition, from: :category) do - skip_category_definition false -end +Fabricator(:category_with_definition, from: :category) { skip_category_definition false } Fabricator(:private_category, from: :category) do transient :group @@ -20,7 +18,10 @@ Fabricator(:private_category, from: :category) do after_build do |cat, transients| cat.update!(read_restricted: true) - cat.category_groups.build(group_id: transients[:group].id, permission_type: transients[:permission_type] || CategoryGroup.permission_types[:full]) + cat.category_groups.build( + group_id: transients[:group].id, + permission_type: transients[:permission_type] || CategoryGroup.permission_types[:full], + ) end end @@ -33,7 +34,7 @@ Fabricator(:link_category, from: :category) do end Fabricator(:mailinglist_mirror_category, from: :category) do - email_in 'list@example.com' + email_in "list@example.com" email_in_allow_strangers true mailinglist_mirror true end diff --git a/spec/fabricators/color_scheme_fabricator.rb b/spec/fabricators/color_scheme_fabricator.rb index 711b0f5e949..539926b71cc 100644 --- a/spec/fabricators/color_scheme_fabricator.rb +++ b/spec/fabricators/color_scheme_fabricator.rb @@ -2,5 +2,7 @@ Fabricator(:color_scheme) do name { sequence(:name) { |i| "Palette #{i}" } } - color_scheme_colors(count: 2) { |attrs, i| Fabricate.build(:color_scheme_color, color_scheme: nil) } + color_scheme_colors(count: 2) do |attrs, i| + Fabricate.build(:color_scheme_color, color_scheme: nil) + end end diff --git a/spec/fabricators/external_upload_stub_fabricator.rb b/spec/fabricators/external_upload_stub_fabricator.rb index 5f8eb3e25e0..e175aa0d258 100644 --- a/spec/fabricators/external_upload_stub_fabricator.rb +++ b/spec/fabricators/external_upload_stub_fabricator.rb @@ -5,7 +5,12 @@ Fabricator(:external_upload_stub) do created_by { Fabricate(:user) } original_filename "test.txt" - key { |attrs| FileStore::BaseStore.temporary_upload_path("test.txt", folder_prefix: attrs[:folder_prefix] || "") } + key do |attrs| + FileStore::BaseStore.temporary_upload_path( + "test.txt", + folder_prefix: attrs[:folder_prefix] || "", + ) + end upload_type "card_background" filesize 1024 status 1 @@ -14,16 +19,28 @@ end Fabricator(:image_external_upload_stub, from: :external_upload_stub) do original_filename "logo.png" filesize 1024 - key { |attrs| FileStore::BaseStore.temporary_upload_path("logo.png", folder_prefix: attrs[:folder_prefix] || "") } + key do |attrs| + FileStore::BaseStore.temporary_upload_path( + "logo.png", + folder_prefix: attrs[:folder_prefix] || "", + ) + end end Fabricator(:attachment_external_upload_stub, from: :external_upload_stub) do original_filename "file.pdf" filesize 1024 - key { |attrs| FileStore::BaseStore.temporary_upload_path("file.pdf", folder_prefix: attrs[:folder_prefix] || "") } + key do |attrs| + FileStore::BaseStore.temporary_upload_path( + "file.pdf", + folder_prefix: attrs[:folder_prefix] || "", + ) + end end Fabricator(:multipart_external_upload_stub, from: :external_upload_stub) do multipart true - external_upload_identifier { "#{SecureRandom.hex(6)}._#{SecureRandom.hex(6)}_#{SecureRandom.hex(6)}.d.ghQ" } + external_upload_identifier do + "#{SecureRandom.hex(6)}._#{SecureRandom.hex(6)}_#{SecureRandom.hex(6)}.d.ghQ" + end end diff --git a/spec/fabricators/group_fabricator.rb b/spec/fabricators/group_fabricator.rb index de9993d3628..c0a13a3f39d 100644 --- a/spec/fabricators/group_fabricator.rb +++ b/spec/fabricators/group_fabricator.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -Fabricator(:group) do - name { sequence(:name) { |n| "my_group_#{n}" } } -end +Fabricator(:group) { name { sequence(:name) { |n| "my_group_#{n}" } } } Fabricator(:public_group, from: :group) do public_admission true diff --git a/spec/fabricators/invite_fabricator.rb b/spec/fabricators/invite_fabricator.rb index aedc23c1772..d8a8747796b 100644 --- a/spec/fabricators/invite_fabricator.rb +++ b/spec/fabricators/invite_fabricator.rb @@ -2,5 +2,5 @@ Fabricator(:invite) do invited_by(fabricator: :user) - email 'iceking@ADVENTURETIME.ooo' + email "iceking@ADVENTURETIME.ooo" end diff --git a/spec/fabricators/muted_user.rb b/spec/fabricators/muted_user.rb index 4bee8414e01..992c205abe8 100644 --- a/spec/fabricators/muted_user.rb +++ b/spec/fabricators/muted_user.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:muted_user) do - user -end +Fabricator(:muted_user) { user } diff --git a/spec/fabricators/notification_fabricator.rb b/spec/fabricators/notification_fabricator.rb index 6ee4c1bcfb9..51d34032e48 100644 --- a/spec/fabricators/notification_fabricator.rb +++ b/spec/fabricators/notification_fabricator.rb @@ -27,7 +27,7 @@ Fabricator(:private_message_notification, from: :notification) do original_post_type: post.post_type, original_username: post.user.username, revision_number: nil, - display_username: post.user.username + display_username: post.user.username, }.to_json end end @@ -44,7 +44,7 @@ Fabricator(:bookmark_reminder_notification, from: :notification) do original_username: post.user.username, revision_number: nil, display_username: post.user.username, - bookmark_name: "Check out Mr Freeze's opinion here" + bookmark_name: "Check out Mr Freeze's opinion here", }.to_json end end @@ -58,7 +58,7 @@ Fabricator(:replied_notification, from: :notification) do original_post_id: post.id, original_username: post.user.username, revision_number: nil, - display_username: post.user.username + display_username: post.user.username, }.to_json end end @@ -73,7 +73,7 @@ Fabricator(:posted_notification, from: :notification) do original_post_type: post.post_type, original_username: post.user.username, revision_number: nil, - display_username: post.user.username + display_username: post.user.username, }.to_json end end @@ -87,7 +87,7 @@ Fabricator(:mentioned_notification, from: :notification) do original_post_type: attrs[:post].post_type, original_username: attrs[:post].user.username, revision_number: nil, - display_username: attrs[:post].user.username + display_username: attrs[:post].user.username, }.to_json end end @@ -101,7 +101,7 @@ Fabricator(:watching_first_post_notification, from: :notification) do original_post_type: attrs[:post].post_type, original_username: attrs[:post].user.username, revision_number: nil, - display_username: attrs[:post].user.username + display_username: attrs[:post].user.username, }.to_json end end diff --git a/spec/fabricators/permalink_fabricator.rb b/spec/fabricators/permalink_fabricator.rb index b2852126064..0f21d1f2e6b 100644 --- a/spec/fabricators/permalink_fabricator.rb +++ b/spec/fabricators/permalink_fabricator.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:permalink) do - url { sequence(:url) { |i| "my/#{i}/url" } } -end +Fabricator(:permalink) { url { sequence(:url) { |i| "my/#{i}/url" } } } diff --git a/spec/fabricators/post_fabricator.rb b/spec/fabricators/post_fabricator.rb index e18628a141a..07e8a734482 100644 --- a/spec/fabricators/post_fabricator.rb +++ b/spec/fabricators/post_fabricator.rb @@ -7,19 +7,17 @@ Fabricator(:post) do post_type Post.types[:regular] # Fabrication bypasses PostCreator, for performance reasons, where the counts are updated so we have to handle this manually here. - after_create do |post, _transients| - UserStatCountUpdater.increment!(post) - end + after_create { |post, _transients| UserStatCountUpdater.increment!(post) } end Fabricator(:post_with_long_raw_content, from: :post) do - raw 'This is a sample post with semi-long raw content. The raw content is also more than + raw "This is a sample post with semi-long raw content. The raw content is also more than two hundred characters to satisfy any test conditions that require content longer - than the typical test post raw content. It really is some long content, folks.' + than the typical test post raw content. It really is some long content, folks." end Fabricator(:post_with_youtube, from: :post) do - raw 'http://www.youtube.com/watch?v=9bZkp7q19f0' + raw "http://www.youtube.com/watch?v=9bZkp7q19f0" cooked '

http://www.youtube.com/watch?v=9bZkp7q19f0

' end @@ -39,7 +37,7 @@ Fabricator(:basic_reply, from: :post) do user(fabricator: :coding_horror) reply_to_post_number 1 topic - raw 'this reply has no quotes' + raw "this reply has no quotes" end Fabricator(:reply, from: :post) do @@ -51,14 +49,12 @@ Fabricator(:reply, from: :post) do ' end -Fabricator(:post_with_plenty_of_images, from: :post) do - cooked <<~HTML +Fabricator(:post_with_plenty_of_images, from: :post) { cooked <<~HTML }

With an emoji! smile

HTML -end Fabricator(:post_with_uploaded_image, from: :post) do raw { "" } @@ -101,8 +97,7 @@ Fabricator(:post_with_uploads, from: :post) do " end -Fabricator(:post_with_uploads_and_links, from: :post) do - raw <<~MD +Fabricator(:post_with_uploads_and_links, from: :post) { raw <<~MD } Link Google @@ -110,7 +105,6 @@ Fabricator(:post_with_uploads_and_links, from: :post) do text.txt (20 Bytes) :smile: MD -end Fabricator(:post_with_external_links, from: :post) do user @@ -130,14 +124,15 @@ Fabricator(:private_message_post, from: :post) do transient :recipient user topic do |attrs| - Fabricate(:private_message_topic, + Fabricate( + :private_message_topic, user: attrs[:user], created_at: attrs[:created_at], subtype: TopicSubtype.user_to_user, topic_allowed_users: [ Fabricate.build(:topic_allowed_user, user: attrs[:user]), - Fabricate.build(:topic_allowed_user, user: attrs[:recipient] || Fabricate(:user)) - ] + Fabricate.build(:topic_allowed_user, user: attrs[:recipient] || Fabricate(:user)), + ], ) end raw "Ssshh! This is our secret conversation!" @@ -147,16 +142,15 @@ Fabricator(:group_private_message_post, from: :post) do transient :recipients user topic do |attrs| - Fabricate(:private_message_topic, + Fabricate( + :private_message_topic, user: attrs[:user], created_at: attrs[:created_at], subtype: TopicSubtype.user_to_user, - topic_allowed_users: [ - Fabricate.build(:topic_allowed_user, user: attrs[:user]), - ], + topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])], topic_allowed_groups: [ - Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group)) - ] + Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group)), + ], ) end raw "Ssshh! This is our group secret conversation!" @@ -165,13 +159,12 @@ end Fabricator(:private_message_post_one_user, from: :post) do user topic do |attrs| - Fabricate(:private_message_topic, + Fabricate( + :private_message_topic, user: attrs[:user], created_at: attrs[:created_at], subtype: TopicSubtype.user_to_user, - topic_allowed_users: [ - Fabricate.build(:topic_allowed_user, user: attrs[:user]), - ] + topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])], ) end raw "Ssshh! This is our secret conversation!" @@ -188,10 +181,6 @@ Fabricator(:post_via_email, from: :post) do end end -Fabricator(:whisper, from: :post) do - post_type Post.types[:whisper] -end +Fabricator(:whisper, from: :post) { post_type Post.types[:whisper] } -Fabricator(:small_action, from: :post) do - post_type Post.types[:small_action] -end +Fabricator(:small_action, from: :post) { post_type Post.types[:small_action] } diff --git a/spec/fabricators/post_revision_fabricator.rb b/spec/fabricators/post_revision_fabricator.rb index 059f1af5a93..848d184f590 100644 --- a/spec/fabricators/post_revision_fabricator.rb +++ b/spec/fabricators/post_revision_fabricator.rb @@ -4,7 +4,5 @@ Fabricator(:post_revision) do post user number 2 - modifications do - { "cooked" => ["

BEFORE

", "

AFTER

"], "raw" => ["BEFORE", "AFTER"] } - end + modifications { { "cooked" => %w[

BEFORE

AFTER

], "raw" => %w[BEFORE AFTER] } } end diff --git a/spec/fabricators/reviewable_fabricator.rb b/spec/fabricators/reviewable_fabricator.rb index b2f26120406..2606a99c291 100644 --- a/spec/fabricators/reviewable_fabricator.rb +++ b/spec/fabricators/reviewable_fabricator.rb @@ -2,70 +2,75 @@ Fabricator(:reviewable) do reviewable_by_moderator true - type 'ReviewableUser' + type "ReviewableUser" created_by { Fabricate(:user) } target_id { Fabricate(:user).id } target_type "User" target_created_by { Fabricate(:user) } category score 1.23 - payload { - { list: [1, 2, 3], name: 'bandersnatch' } - } + payload { { list: [1, 2, 3], name: "bandersnatch" } } end Fabricator(:reviewable_queued_post_topic, class_name: :reviewable_queued_post) do reviewable_by_moderator true - type 'ReviewableQueuedPost' + type "ReviewableQueuedPost" created_by { Fabricate(:user) } category - payload { + payload do { raw: "hello world post contents.", title: "queued post title", - tags: ['cool', 'neat'], + tags: %w[cool neat], extra: "some extra data", - archetype: 'regular' + archetype: "regular", } - } + end end Fabricator(:reviewable_queued_post) do reviewable_by_moderator true - type 'ReviewableQueuedPost' + type "ReviewableQueuedPost" created_by { Fabricate(:user) } topic - payload { + payload do { raw: "hello world post contents.", reply_to_post_number: 1, via_email: true, - raw_email: 'store_me', + raw_email: "store_me", auto_track: true, - custom_fields: { hello: 'world' }, - cooking_options: { cat: 'hat' }, + custom_fields: { + hello: "world", + }, + cooking_options: { + cat: "hat", + }, cook_method: Post.cook_methods[:raw_html], - image_sizes: { "http://foo.bar/image.png" => { "width" => 0, "height" => 222 } } + image_sizes: { + "http://foo.bar/image.png" => { + "width" => 0, + "height" => 222, + }, + }, } - } + end end Fabricator(:reviewable_flagged_post) do reviewable_by_moderator true - type 'ReviewableFlaggedPost' + type "ReviewableFlaggedPost" created_by { Fabricate(:user) } topic - target_type 'Post' + target_type "Post" target { Fabricate(:post) } - reviewable_scores { |p| [ - Fabricate.build(:reviewable_score, reviewable_id: p[:id]), - ]} + reviewable_scores { |p| [Fabricate.build(:reviewable_score, reviewable_id: p[:id])] } end Fabricator(:reviewable_user) do reviewable_by_moderator true - type 'ReviewableUser' + type "ReviewableUser" created_by { Fabricate(:user) } - target_type 'User' + target_type "User" target { Fabricate(:user) } end diff --git a/spec/fabricators/screened_url_fabricator.rb b/spec/fabricators/screened_url_fabricator.rb index 8533946c65a..7225852a5d6 100644 --- a/spec/fabricators/screened_url_fabricator.rb +++ b/spec/fabricators/screened_url_fabricator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true Fabricator(:screened_url) do - url { sequence(:url) { |n| "spammers#{n}.org/buy/stuff" } } - domain { sequence(:domain) { |n| "spammers#{n}.org" } } + url { sequence(:url) { |n| "spammers#{n}.org/buy/stuff" } } + domain { sequence(:domain) { |n| "spammers#{n}.org" } } action_type ScreenedEmail.actions[:do_nothing] end diff --git a/spec/fabricators/sidebar_section_link_fabricator.rb b/spec/fabricators/sidebar_section_link_fabricator.rb index 4a5d7683807..569de894fe0 100644 --- a/spec/fabricators/sidebar_section_link_fabricator.rb +++ b/spec/fabricators/sidebar_section_link_fabricator.rb @@ -1,13 +1,9 @@ # frozen_string_literal: true -Fabricator(:sidebar_section_link) do - user -end +Fabricator(:sidebar_section_link) { user } Fabricator(:category_sidebar_section_link, from: :sidebar_section_link) do linkable(fabricator: :category) end -Fabricator(:tag_sidebar_section_link, from: :sidebar_section_link) do - linkable(fabricator: :tag) -end +Fabricator(:tag_sidebar_section_link, from: :sidebar_section_link) { linkable(fabricator: :tag) } diff --git a/spec/fabricators/single_sign_on_record_fabricator.rb b/spec/fabricators/single_sign_on_record_fabricator.rb index 95c6a6028f4..1afbc727062 100644 --- a/spec/fabricators/single_sign_on_record_fabricator.rb +++ b/spec/fabricators/single_sign_on_record_fabricator.rb @@ -5,5 +5,7 @@ Fabricator(:single_sign_on_record) do external_id { sequence(:external_id) { |i| "ext_#{i}" } } external_username { sequence(:username) { |i| "bruce#{i}" } } external_email { sequence(:email) { |i| "bruce#{i}@wayne.com" } } - last_payload { sequence(:last_payload) { |i| "nonce=#{i}1870a940bbcbb46f06880ed338d58a07&name=" } } + last_payload do + sequence(:last_payload) { |i| "nonce=#{i}1870a940bbcbb46f06880ed338d58a07&name=" } + end end diff --git a/spec/fabricators/tag_fabricator.rb b/spec/fabricators/tag_fabricator.rb index c2192294efb..6d68de48f4d 100644 --- a/spec/fabricators/tag_fabricator.rb +++ b/spec/fabricators/tag_fabricator.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:tag) do - name { sequence(:name) { |i| "tag#{i + 1}" } } -end +Fabricator(:tag) { name { sequence(:name) { |i| "tag#{i + 1}" } } } diff --git a/spec/fabricators/tag_group_fabricator.rb b/spec/fabricators/tag_group_fabricator.rb index 990ec85d72a..64a38f9004b 100644 --- a/spec/fabricators/tag_group_fabricator.rb +++ b/spec/fabricators/tag_group_fabricator.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:tag_group) do - name { sequence(:name) { |i| "tag_group_#{i}" } } -end +Fabricator(:tag_group) { name { sequence(:name) { |i| "tag_group_#{i}" } } } diff --git a/spec/fabricators/topic_allowed_user_fabricator.rb b/spec/fabricators/topic_allowed_user_fabricator.rb index eb3d75f6e26..99494eb170c 100644 --- a/spec/fabricators/topic_allowed_user_fabricator.rb +++ b/spec/fabricators/topic_allowed_user_fabricator.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:topic_allowed_user) do - user -end +Fabricator(:topic_allowed_user) { user } diff --git a/spec/fabricators/topic_fabricator.rb b/spec/fabricators/topic_fabricator.rb index b65cd48d7fa..c693fc213c6 100644 --- a/spec/fabricators/topic_fabricator.rb +++ b/spec/fabricators/topic_fabricator.rb @@ -8,25 +8,21 @@ Fabricator(:topic) do end end -Fabricator(:deleted_topic, from: :topic) do - deleted_at { 1.minute.ago } -end +Fabricator(:deleted_topic, from: :topic) { deleted_at { 1.minute.ago } } -Fabricator(:closed_topic, from: :topic) do - closed true -end +Fabricator(:closed_topic, from: :topic) { closed true } -Fabricator(:banner_topic, from: :topic) do - archetype Archetype.banner -end +Fabricator(:banner_topic, from: :topic) { archetype Archetype.banner } Fabricator(:private_message_topic, from: :topic) do transient :recipient category_id { nil } title { sequence(:title) { |i| "This is a private message #{i}" } } archetype "private_message" - topic_allowed_users { |t| [ - Fabricate.build(:topic_allowed_user, user: t[:user]), - Fabricate.build(:topic_allowed_user, user: t[:recipient] || Fabricate(:user)) - ]} + topic_allowed_users do |t| + [ + Fabricate.build(:topic_allowed_user, user: t[:user]), + Fabricate.build(:topic_allowed_user, user: t[:recipient] || Fabricate(:user)), + ] + end end diff --git a/spec/fabricators/upload_fabricator.rb b/spec/fabricators/upload_fabricator.rb index cb47f293b09..aec9a20721d 100644 --- a/spec/fabricators/upload_fabricator.rb +++ b/spec/fabricators/upload_fabricator.rb @@ -12,9 +12,7 @@ Fabricator(:upload) do url do |attrs| sequence(:url) do |n| - Discourse.store.get_path_for( - "original", n + 1, attrs[:sha1], ".#{attrs[:extension]}" - ) + Discourse.store.get_path_for("original", n + 1, attrs[:sha1], ".#{attrs[:extension]}") end end @@ -35,15 +33,16 @@ Fabricator(:image_upload, from: :upload) do transient color: "white" after_create do |upload, transients| - file = Tempfile.new(['fabricated', '.png']) + file = Tempfile.new(%w[fabricated .png]) `convert -size #{upload.width}x#{upload.height} xc:#{transients[:color]} "#{file.path}"` upload.url = Discourse.store.store_upload(file, upload) upload.sha1 = Upload.generate_digest(file.path) - WebMock - .stub_request(:get, "http://#{Discourse.current_hostname}#{upload.url}") - .to_return(status: 200, body: File.new(file.path)) + WebMock.stub_request(:get, "http://#{Discourse.current_hostname}#{upload.url}").to_return( + status: 200, + body: File.new(file.path), + ) end end @@ -72,13 +71,9 @@ end Fabricator(:upload_s3, from: :upload) do url do |attrs| sequence(:url) do |n| - path = +Discourse.store.get_path_for( - "original", n + 1, attrs[:sha1], ".#{attrs[:extension]}" - ) + path = +Discourse.store.get_path_for("original", n + 1, attrs[:sha1], ".#{attrs[:extension]}") - if Rails.configuration.multisite - path.prepend(File.join(Discourse.store.upload_path, "/")) - end + path.prepend(File.join(Discourse.store.upload_path, "/")) if Rails.configuration.multisite File.join(Discourse.store.absolute_base_url, path) end @@ -87,15 +82,13 @@ end Fabricator(:s3_image_upload, from: :upload_s3) do after_create do |upload| - file = Tempfile.new(['fabricated', '.png']) + file = Tempfile.new(%w[fabricated .png]) `convert -size #{upload.width}x#{upload.height} xc:white "#{file.path}"` upload.url = Discourse.store.store_upload(file, upload) upload.sha1 = Upload.generate_digest(file.path) - WebMock - .stub_request(:get, upload.url) - .to_return(status: 200, body: File.new(file.path)) + WebMock.stub_request(:get, upload.url).to_return(status: 200, body: File.new(file.path)) end end diff --git a/spec/fabricators/user_api_key_fabricator.rb b/spec/fabricators/user_api_key_fabricator.rb index 3ca55431c8e..fc2e805ecb2 100644 --- a/spec/fabricators/user_api_key_fabricator.rb +++ b/spec/fabricators/user_api_key_fabricator.rb @@ -3,15 +3,15 @@ Fabricator(:user_api_key) do user client_id { SecureRandom.hex } - application_name 'some app' + application_name "some app" end Fabricator(:user_api_key_scope) Fabricator(:readonly_user_api_key, from: :user_api_key) do - scopes { [Fabricate.build(:user_api_key_scope, name: 'read')] } + scopes { [Fabricate.build(:user_api_key_scope, name: "read")] } end Fabricator(:bookmarks_calendar_user_api_key, from: :user_api_key) do - scopes { [Fabricate.build(:user_api_key_scope, name: 'bookmarks_calendar')] } + scopes { [Fabricate.build(:user_api_key_scope, name: "bookmarks_calendar")] } end diff --git a/spec/fabricators/user_avatar_fabricator.rb b/spec/fabricators/user_avatar_fabricator.rb index f7431bcbb17..773947f04b9 100644 --- a/spec/fabricators/user_avatar_fabricator.rb +++ b/spec/fabricators/user_avatar_fabricator.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -Fabricator(:user_avatar) do - user -end +Fabricator(:user_avatar) { user } diff --git a/spec/fabricators/user_fabricator.rb b/spec/fabricators/user_fabricator.rb index bee8812bd89..1a0fcca19a6 100644 --- a/spec/fabricators/user_fabricator.rb +++ b/spec/fabricators/user_fabricator.rb @@ -1,13 +1,12 @@ # frozen_string_literal: true -Fabricator(:user_stat) do -end +Fabricator(:user_stat) {} Fabricator(:user, class_name: :user) do - name 'Bruce Wayne' + name "Bruce Wayne" username { sequence(:username) { |i| "bruce#{i}" } } email { sequence(:email) { |i| "bruce#{i}@wayne.com" } } - password 'myawesomepassword' + password "myawesomepassword" trust_level TrustLevel[1] ip_address { sequence(:ip_address) { |i| "99.232.23.#{i % 254}" } } active true @@ -18,31 +17,31 @@ Fabricator(:user_with_secondary_email, from: :user) do end Fabricator(:coding_horror, from: :user) do - name 'Coding Horror' - username 'CodingHorror' - email 'jeff@somewhere.com' - password 'mymoreawesomepassword' + name "Coding Horror" + username "CodingHorror" + email "jeff@somewhere.com" + password "mymoreawesomepassword" end Fabricator(:evil_trout, from: :user) do - name 'Evil Trout' - username 'eviltrout' - email 'eviltrout@somewhere.com' - password 'imafish123' + name "Evil Trout" + username "eviltrout" + email "eviltrout@somewhere.com" + password "imafish123" end Fabricator(:walter_white, from: :user) do - name 'Walter White' - username 'heisenberg' - email 'wwhite@bluemeth.com' - password 'letscook123' + name "Walter White" + username "heisenberg" + email "wwhite@bluemeth.com" + password "letscook123" end Fabricator(:inactive_user, from: :user) do - name 'Inactive User' - username 'inactive_user' - email 'inactive@idontexist.com' - password 'qwerqwer123' + name "Inactive User" + username "inactive_user" + email "inactive@idontexist.com" + password "qwerqwer123" active false end @@ -59,7 +58,7 @@ Fabricator(:moderator, from: :user) do end Fabricator(:admin, from: :user) do - name 'Anne Admin' + name "Anne Admin" username { sequence(:username) { |i| "anne#{i}" } } email { sequence(:email) { |i| "anne#{i}@discourse.org" } } admin true @@ -71,17 +70,17 @@ Fabricator(:admin, from: :user) do end Fabricator(:newuser, from: :user) do - name 'Newbie Newperson' - username 'newbie' - email 'newbie@new.com' + name "Newbie Newperson" + username "newbie" + email "newbie@new.com" trust_level TrustLevel[0] end Fabricator(:active_user, from: :user) do - name 'Luke Skywalker' + name "Luke Skywalker" username { sequence(:username) { |i| "luke#{i}" } } email { sequence(:email) { |i| "luke#{i}@skywalker.com" } } - password 'myawesomepassword' + password "myawesomepassword" trust_level TrustLevel[1] after_create do |user| @@ -91,29 +90,25 @@ Fabricator(:active_user, from: :user) do end Fabricator(:leader, from: :user) do - name 'Veteran McVeteranish' + name "Veteran McVeteranish" username { sequence(:username) { |i| "leader#{i}" } } email { sequence(:email) { |i| "leader#{i}@leaderfun.com" } } trust_level TrustLevel[3] end -Fabricator(:trust_level_0, from: :user) do - trust_level TrustLevel[0] -end +Fabricator(:trust_level_0, from: :user) { trust_level TrustLevel[0] } -Fabricator(:trust_level_1, from: :user) do - trust_level TrustLevel[1] -end +Fabricator(:trust_level_1, from: :user) { trust_level TrustLevel[1] } Fabricator(:trust_level_4, from: :user) do - name 'Leader McElderson' + name "Leader McElderson" username { sequence(:username) { |i| "tl4#{i}" } } email { sequence(:email) { |i| "tl4#{i}@elderfun.com" } } trust_level TrustLevel[4] end Fabricator(:anonymous, from: :user) do - name '' + name "" username { sequence(:username) { |i| "anonymous#{i}" } } email { sequence(:email) { |i| "anonymous#{i}@anonymous.com" } } trust_level TrustLevel[1] @@ -127,13 +122,9 @@ Fabricator(:anonymous, from: :user) do end end -Fabricator(:staged, from: :user) do - staged true -end +Fabricator(:staged, from: :user) { staged true } -Fabricator(:unicode_user, from: :user) do - username { sequence(:username) { |i| "Löwe#{i}" } } -end +Fabricator(:unicode_user, from: :user) { username { sequence(:username) { |i| "Löwe#{i}" } } } Fabricator(:bot, from: :user) do id do diff --git a/spec/fabricators/user_field_fabricator.rb b/spec/fabricators/user_field_fabricator.rb index c8019b390af..8534e80657c 100644 --- a/spec/fabricators/user_field_fabricator.rb +++ b/spec/fabricators/user_field_fabricator.rb @@ -3,7 +3,7 @@ Fabricator(:user_field) do name { sequence(:name) { |i| "field_#{i}" } } description "user field description" - field_type 'text' + field_type "text" editable true required true end diff --git a/spec/fabricators/user_history_fabricator.rb b/spec/fabricators/user_history_fabricator.rb index 4464b03c7cd..72bdd160754 100644 --- a/spec/fabricators/user_history_fabricator.rb +++ b/spec/fabricators/user_history_fabricator.rb @@ -1,4 +1,3 @@ # frozen_string_literal: true -Fabricator(:user_history) do -end +Fabricator(:user_history) {} diff --git a/spec/fabricators/user_option_fabricator.rb b/spec/fabricators/user_option_fabricator.rb index 17c0cbc788e..77e55384a8a 100644 --- a/spec/fabricators/user_option_fabricator.rb +++ b/spec/fabricators/user_option_fabricator.rb @@ -1,4 +1,3 @@ # frozen_string_literal: true -Fabricator(:user_option) do -end +Fabricator(:user_option) {} diff --git a/spec/fabricators/user_profile_fabricator.rb b/spec/fabricators/user_profile_fabricator.rb index 042474ed8bc..ada2483eebe 100644 --- a/spec/fabricators/user_profile_fabricator.rb +++ b/spec/fabricators/user_profile_fabricator.rb @@ -5,6 +5,4 @@ Fabricator(:user_profile) do user end -Fabricator(:user_profile_long, from: :user_profile) do - bio_raw ("trout" * 1000) -end +Fabricator(:user_profile_long, from: :user_profile) { bio_raw ("trout" * 1000) } diff --git a/spec/fabricators/user_second_factor_fabricator.rb b/spec/fabricators/user_second_factor_fabricator.rb index cbb2d5aa4ae..8d4a5176318 100644 --- a/spec/fabricators/user_second_factor_fabricator.rb +++ b/spec/fabricators/user_second_factor_fabricator.rb @@ -2,7 +2,7 @@ Fabricator(:user_second_factor_totp, from: :user_second_factor) do user - data 'rcyryaqage3jexfj' + data "rcyryaqage3jexfj" enabled true method UserSecondFactor.methods[:totp] end diff --git a/spec/fabricators/user_security_key_fabricator.rb b/spec/fabricators/user_security_key_fabricator.rb index 827413a18de..edc4279f60c 100644 --- a/spec/fabricators/user_security_key_fabricator.rb +++ b/spec/fabricators/user_security_key_fabricator.rb @@ -5,8 +5,12 @@ Fabricator(:user_security_key) do # Note: these values are valid and decode to a credential ID and COSE public key # HOWEVER they are largely useless unless you have the device that created # them. It is nice to have an approximation though. - credential_id { 'mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A==' } - public_key { 'pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU=' } + credential_id do + "mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A==" + end + public_key do + "pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU=" + end enabled true factor_type { UserSecurityKey.factor_types[:second_factor] } name { sequence(:name) { |i| "Security Key #{i + 1}" } } diff --git a/spec/fabricators/web_hook_fabricator.rb b/spec/fabricators/web_hook_fabricator.rb index 3e490b3f4cb..97f753bd134 100644 --- a/spec/fabricators/web_hook_fabricator.rb +++ b/spec/fabricators/web_hook_fabricator.rb @@ -1,62 +1,48 @@ # frozen_string_literal: true Fabricator(:web_hook) do - payload_url 'https://meta.discourse.org/webhook_listener' - content_type WebHook.content_types['application/json'] + payload_url "https://meta.discourse.org/webhook_listener" + content_type WebHook.content_types["application/json"] wildcard_web_hook false - secret 'my_lovely_secret_for_web_hook' + secret "my_lovely_secret_for_web_hook" verify_certificate true active true - transient post_hook: WebHookEventType.find_by(name: 'post') + transient post_hook: WebHookEventType.find_by(name: "post") - after_build do |web_hook, transients| - web_hook.web_hook_event_types << transients[:post_hook] - end + after_build { |web_hook, transients| web_hook.web_hook_event_types << transients[:post_hook] } end -Fabricator(:inactive_web_hook, from: :web_hook) do - active false -end +Fabricator(:inactive_web_hook, from: :web_hook) { active false } -Fabricator(:wildcard_web_hook, from: :web_hook) do - wildcard_web_hook true -end +Fabricator(:wildcard_web_hook, from: :web_hook) { wildcard_web_hook true } Fabricator(:topic_web_hook, from: :web_hook) do - transient topic_hook: WebHookEventType.find_by(name: 'topic') + transient topic_hook: WebHookEventType.find_by(name: "topic") - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:topic_hook]] - end + after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:topic_hook]] } end Fabricator(:post_web_hook, from: :web_hook) do - transient topic_hook: WebHookEventType.find_by(name: 'post') + transient topic_hook: WebHookEventType.find_by(name: "post") - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:post_hook]] - end + after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:post_hook]] } end Fabricator(:user_web_hook, from: :web_hook) do - transient user_hook: WebHookEventType.find_by(name: 'user') + transient user_hook: WebHookEventType.find_by(name: "user") - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:user_hook]] - end + after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:user_hook]] } end Fabricator(:group_web_hook, from: :web_hook) do - transient group_hook: WebHookEventType.find_by(name: 'group') + transient group_hook: WebHookEventType.find_by(name: "group") - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:group_hook]] - end + after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:group_hook]] } end Fabricator(:category_web_hook, from: :web_hook) do - transient category_hook: WebHookEventType.find_by(name: 'category') + transient category_hook: WebHookEventType.find_by(name: "category") after_build do |web_hook, transients| web_hook.web_hook_event_types = [transients[:category_hook]] @@ -64,15 +50,13 @@ Fabricator(:category_web_hook, from: :web_hook) do end Fabricator(:tag_web_hook, from: :web_hook) do - transient tag_hook: WebHookEventType.find_by(name: 'tag') + transient tag_hook: WebHookEventType.find_by(name: "tag") - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:tag_hook]] - end + after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:tag_hook]] } end Fabricator(:reviewable_web_hook, from: :web_hook) do - transient reviewable_hook: WebHookEventType.find_by(name: 'reviewable') + transient reviewable_hook: WebHookEventType.find_by(name: "reviewable") after_build do |web_hook, transients| web_hook.web_hook_event_types = [transients[:reviewable_hook]] @@ -80,7 +64,7 @@ Fabricator(:reviewable_web_hook, from: :web_hook) do end Fabricator(:notification_web_hook, from: :web_hook) do - transient notification_hook: WebHookEventType.find_by(name: 'notification') + transient notification_hook: WebHookEventType.find_by(name: "notification") after_build do |web_hook, transients| web_hook.web_hook_event_types = [transients[:notification_hook]] @@ -88,7 +72,7 @@ Fabricator(:notification_web_hook, from: :web_hook) do end Fabricator(:user_badge_web_hook, from: :web_hook) do - transient user_badge_hook: WebHookEventType.find_by(name: 'user_badge') + transient user_badge_hook: WebHookEventType.find_by(name: "user_badge") after_build do |web_hook, transients| web_hook.web_hook_event_types = [transients[:user_badge_hook]] @@ -96,7 +80,7 @@ Fabricator(:user_badge_web_hook, from: :web_hook) do end Fabricator(:group_user_web_hook, from: :web_hook) do - transient group_user_hook: WebHookEventType.find_by(name: 'group_user') + transient group_user_hook: WebHookEventType.find_by(name: "group_user") after_build do |web_hook, transients| web_hook.web_hook_event_types = [transients[:group_user_hook]] @@ -104,15 +88,13 @@ Fabricator(:group_user_web_hook, from: :web_hook) do end Fabricator(:like_web_hook, from: :web_hook) do - transient like_hook: WebHookEventType.find_by(name: 'like') + transient like_hook: WebHookEventType.find_by(name: "like") - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:like_hook]] - end + after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:like_hook]] } end Fabricator(:user_promoted_web_hook, from: :web_hook) do - transient user_promoted_hook: WebHookEventType.find_by(name: 'user_promoted') + transient user_promoted_hook: WebHookEventType.find_by(name: "user_promoted") after_build do |web_hook, transients| web_hook.web_hook_event_types = [transients[:user_promoted_hook]] diff --git a/spec/fixtures/db/post_migrate/change/20990309014015_drop_email_logs.rb b/spec/fixtures/db/post_migrate/change/20990309014015_drop_email_logs.rb index 7b4b1e2252d..441440772af 100644 --- a/spec/fixtures/db/post_migrate/change/20990309014015_drop_email_logs.rb +++ b/spec/fixtures/db/post_migrate/change/20990309014015_drop_email_logs.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class DropEmailLogs < ActiveRecord::Migration[5.2] - DROPPED_TABLES ||= %i{email_logs} + DROPPED_TABLES ||= %i[email_logs] def change drop_table :email_logs diff --git a/spec/fixtures/db/post_migrate/drop_column/20990309014014_drop_post_columns.rb b/spec/fixtures/db/post_migrate/drop_column/20990309014014_drop_post_columns.rb index 8390f83207d..556489f2553 100644 --- a/spec/fixtures/db/post_migrate/drop_column/20990309014014_drop_post_columns.rb +++ b/spec/fixtures/db/post_migrate/drop_column/20990309014014_drop_post_columns.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true class DropPostColumns < ActiveRecord::Migration[5.2] - DROPPED_COLUMNS ||= { - posts: %i{via_email raw_email} - } + DROPPED_COLUMNS ||= { posts: %i[via_email raw_email] } def up remove_column :posts, :via_email diff --git a/spec/fixtures/db/post_migrate/drop_table/20990309014013_drop_email_logs_table.rb b/spec/fixtures/db/post_migrate/drop_table/20990309014013_drop_email_logs_table.rb index 5d07960ea7a..ed8d31d7e64 100644 --- a/spec/fixtures/db/post_migrate/drop_table/20990309014013_drop_email_logs_table.rb +++ b/spec/fixtures/db/post_migrate/drop_table/20990309014013_drop_email_logs_table.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class DropEmailLogsTable < ActiveRecord::Migration[5.2] - DROPPED_TABLES ||= %i{email_logs} + DROPPED_TABLES ||= %i[email_logs] def up drop_table :email_logs diff --git a/spec/fixtures/plugins/csp_extension/plugin.rb b/spec/fixtures/plugins/csp_extension/plugin.rb index e694598c4a1..0991754eb60 100644 --- a/spec/fixtures/plugins/csp_extension/plugin.rb +++ b/spec/fixtures/plugins/csp_extension/plugin.rb @@ -6,8 +6,8 @@ # authors: xrav3nz extend_content_security_policy( - script_src: ['https://from-plugin.com', '/local/path'], - object_src: ['https://test-stripping.com'], - frame_ancestors: ['https://frame-ancestors-plugin.ext'], - manifest_src: ['https://manifest-src.com'] + script_src: %w[https://from-plugin.com /local/path], + object_src: ["https://test-stripping.com"], + frame_ancestors: ["https://frame-ancestors-plugin.ext"], + manifest_src: ["https://manifest-src.com"], ) diff --git a/spec/fixtures/plugins/my_plugin/plugin.rb b/spec/fixtures/plugins/my_plugin/plugin.rb index 2253f3a1b03..62c7ed4dc5b 100644 --- a/spec/fixtures/plugins/my_plugin/plugin.rb +++ b/spec/fixtures/plugins/my_plugin/plugin.rb @@ -5,8 +5,7 @@ # version: 0.1 # authors: Frank Zappa -auth_provider title: 'with Ubuntu', - authenticator: Auth::FacebookAuthenticator.new +auth_provider title: "with Ubuntu", authenticator: Auth::FacebookAuthenticator.new register_javascript < HTML - SiteSetting.opengraph_image = Fabricate(:upload, - url: '/images/og-image.svg' - ) + SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.svg") expect(helper.crawlable_meta_data).to include(<<~HTML) HTML - SiteSetting.twitter_summary_large_image = Fabricate(:upload, - url: '/images/twitter.png' - ) + SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.png") expect(helper.crawlable_meta_data).to include(<<~HTML) HTML - SiteSetting.twitter_summary_large_image = Fabricate(:upload, - url: '/images/twitter.svg' - ) + SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.svg") expect(helper.crawlable_meta_data).to include(<<~HTML) HTML - SiteSetting.logo = Fabricate(:upload, url: '/images/d-logo-sketch.svg') + SiteSetting.logo = Fabricate(:upload, url: "/images/d-logo-sketch.svg") expect(helper.crawlable_meta_data).not_to include("twitter:image") end end end - describe 'discourse_color_scheme_stylesheets' do + describe "discourse_color_scheme_stylesheets" do fab!(:user) { Fabricate(:user) } - it 'returns a stylesheet link tag by default' do + it "returns a stylesheet link tag by default" do cs_stylesheets = helper.discourse_color_scheme_stylesheets expect(cs_stylesheets).to include("stylesheets/color_definitions") end - it 'returns two color scheme link tags when dark mode is enabled' do - SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck_first(:id) + it "returns two color scheme link tags when dark mode is enabled" do + SiteSetting.default_dark_mode_color_scheme_id = + ColorScheme.where(name: "Dark").pluck_first(:id) cs_stylesheets = helper.discourse_color_scheme_stylesheets expect(cs_stylesheets).to include("(prefers-color-scheme: dark)") expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2) end - it 'handles a missing dark color scheme gracefully' do + it "handles a missing dark color scheme gracefully" do scheme = ColorScheme.create!(name: "pyramid") SiteSetting.default_dark_mode_color_scheme_id = scheme.id scheme.destroy! @@ -638,7 +671,7 @@ RSpec.describe ApplicationHelper do context "with custom light scheme" do before do - @new_cs = Fabricate(:color_scheme, name: 'Flamboyant') + @new_cs = Fabricate(:color_scheme, name: "Flamboyant") user.user_option.color_scheme_id = @new_cs.id user.user_option.save! helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user @@ -673,9 +706,10 @@ RSpec.describe ApplicationHelper do user.user_option.dark_scheme_id = -1 user.user_option.save! helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user - @new_cs = Fabricate(:color_scheme, name: 'Custom Color Scheme') + @new_cs = Fabricate(:color_scheme, name: "Custom Color Scheme") - SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck_first(:id) + SiteSetting.default_dark_mode_color_scheme_id = + ColorScheme.where(name: "Dark").pluck_first(:id) end it "returns no dark scheme stylesheet when user has disabled that option" do @@ -711,23 +745,24 @@ RSpec.describe ApplicationHelper do end describe "dark_color_scheme?" do - it 'returns false for the base color scheme' do + it "returns false for the base color scheme" do expect(helper.dark_color_scheme?).to eq(false) end - it 'works correctly for a dark scheme' do - dark_theme = Theme.create( - name: "Dark", - user_id: -1, - color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id - ) + it "works correctly for a dark scheme" do + dark_theme = + Theme.create( + name: "Dark", + user_id: -1, + color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id, + ) helper.request.env[:resolved_theme_id] = dark_theme.id expect(helper.dark_color_scheme?).to eq(true) end end - describe 'html_lang' do + describe "html_lang" do fab!(:user) { Fabricate(:user) } before do @@ -735,12 +770,12 @@ RSpec.describe ApplicationHelper do SiteSetting.default_locale = :fr end - it 'returns default locale if no request' do + it "returns default locale if no request" do helper.request = nil expect(helper.html_lang).to eq(SiteSetting.default_locale) end - it 'returns current user locale if request' do + it "returns current user locale if request" do helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user expect(helper.html_lang).to eq(I18n.locale.to_s) end diff --git a/spec/helpers/redis_snapshot_helper.rb b/spec/helpers/redis_snapshot_helper.rb index 92d5be9cabd..fb7e6a9b40f 100644 --- a/spec/helpers/redis_snapshot_helper.rb +++ b/spec/helpers/redis_snapshot_helper.rb @@ -2,12 +2,8 @@ module RedisSnapshotHelper def use_redis_snapshotting - before(:each) do - RedisSnapshot.begin_faux_transaction - end + before(:each) { RedisSnapshot.begin_faux_transaction } - after(:each) do - RedisSnapshot.end_faux_transaction - end + after(:each) { RedisSnapshot.end_faux_transaction } end end diff --git a/spec/helpers/topics_helper_spec.rb b/spec/helpers/topics_helper_spec.rb index 5f0462fd97c..075770839a1 100644 --- a/spec/helpers/topics_helper_spec.rb +++ b/spec/helpers/topics_helper_spec.rb @@ -1,13 +1,14 @@ # frozen_string_literal: true RSpec.describe TopicsHelper do - describe "#categories_breadcrumb" do let(:user) { Fabricate(:user) } let(:category) { Fabricate(:category_with_definition) } let(:subcategory) { Fabricate(:category_with_definition, parent_category_id: category.id) } - let(:subsubcategory) { Fabricate(:category_with_definition, parent_category_id: subcategory.id) } + let(:subsubcategory) do + Fabricate(:category_with_definition, parent_category_id: subcategory.id) + end it "works with sub-sub-categories" do SiteSetting.max_category_nesting = 3 diff --git a/spec/helpers/user_notifications_helper_spec.rb b/spec/helpers/user_notifications_helper_spec.rb index 175c7bbb641..94ef066d9ab 100644 --- a/spec/helpers/user_notifications_helper_spec.rb +++ b/spec/helpers/user_notifications_helper_spec.rb @@ -3,18 +3,17 @@ RSpec.describe UserNotificationsHelper do let(:upload_path) { Discourse.store.upload_path } - describe '#email_excerpt' do - let(:paragraphs) { [ - "

This is the first paragraph, but you should read more.

", - "

And here is its friend, the second paragraph.

" - ] } - - let(:cooked) do - paragraphs.join("\n") + describe "#email_excerpt" do + let(:paragraphs) do + [ + "

This is the first paragraph, but you should read more.

", + "

And here is its friend, the second paragraph.

", + ] end - let(:post_quote) do - <<~HTML + let(:cooked) { paragraphs.join("\n") } + + let(:post_quote) { <<~HTML } HTML - end let(:image_paragraph) do '

' end - let(:lightbox_image) do - <<~HTML + let(:lightbox_image) { <<~HTML }

HTML - end let(:expected_lightbox_image) do '' @@ -53,14 +49,16 @@ RSpec.describe UserNotificationsHelper do end it "doesn't count emoji images" do - with_emoji = "

Hi \":smile:\"

" + with_emoji = + "

Hi \":smile:\"

" arg = ([with_emoji] + paragraphs).join("\n") SiteSetting.digest_min_excerpt_length = 50 expect(helper.email_excerpt(arg)).to eq([with_emoji, paragraphs[0]].join) end it "only counts link text" do - with_link = "

Hi friends!

" + with_link = + "

Hi friends!

" arg = ([with_link] + paragraphs).join("\n") SiteSetting.digest_min_excerpt_length = 50 expect(helper.email_excerpt(arg)).to eq([with_link, paragraphs[0]].join) @@ -81,11 +79,12 @@ RSpec.describe UserNotificationsHelper do

AFTER

HTML - expect(helper.email_excerpt(cooked)).to eq "

BEFORE

\n

This is a user quote

\n

AFTER

" + expect( + helper.email_excerpt(cooked), + ).to eq "

BEFORE

\n

This is a user quote

\n

AFTER

" end it "defaults to content after post quote (image w/ no text)" do - cooked = <<~HTML #{post_quote} #{image_paragraph} @@ -94,7 +93,8 @@ RSpec.describe UserNotificationsHelper do end it "defaults to content after post quote (onebox)" do - aside_onebox = '' + aside_onebox = + '' cooked = <<~HTML #{post_quote} #{aside_onebox} @@ -120,44 +120,40 @@ RSpec.describe UserNotificationsHelper do end end - describe '#logo_url' do - describe 'local store' do + describe "#logo_url" do + describe "local store" do let(:upload) { Fabricate(:upload, sha1: "somesha1") } - before do - SiteSetting.logo = upload - end + before { SiteSetting.logo = upload } - it 'should return the right URL' do + it "should return the right URL" do expect(helper.logo_url).to eq( - "http://test.localhost/#{upload_path}/original/1X/somesha1.png" + "http://test.localhost/#{upload_path}/original/1X/somesha1.png", ) end - describe 'when cdn path is configured' do + describe "when cdn path is configured" do before do - GlobalSetting.expects(:cdn_url) - .returns('https://some.localcdn.com') - .at_least_once + GlobalSetting.expects(:cdn_url).returns("https://some.localcdn.com").at_least_once end - it 'should return the right URL' do + it "should return the right URL" do expect(helper.logo_url).to eq( - "https://some.localcdn.com/#{upload_path}/original/1X/somesha1.png" + "https://some.localcdn.com/#{upload_path}/original/1X/somesha1.png", ) end end - describe 'when logo is an SVG' do + describe "when logo is an SVG" do let(:upload) { Fabricate(:upload, extension: "svg") } - it 'should return nil' do + it "should return nil" do expect(helper.logo_url).to eq(nil) end end end - describe 's3 store' do + describe "s3 store" do let(:upload) { Fabricate(:upload_s3, sha1: "somesha1") } before do @@ -165,32 +161,27 @@ RSpec.describe UserNotificationsHelper do SiteSetting.logo = upload end - it 'should return the right URL' do + it "should return the right URL" do expect(helper.logo_url).to eq( - "http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png" + "http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png", ) end - describe 'when global cdn path is configured' do - it 'should return the right url' do - GlobalSetting.stubs(:cdn_url).returns('https://some.cdn.com/cluster') + describe "when global cdn path is configured" do + it "should return the right url" do + GlobalSetting.stubs(:cdn_url).returns("https://some.cdn.com/cluster") expect(helper.logo_url).to eq( - "http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png" + "http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png", ) end end - describe 'when cdn path is configured' do - before do - SiteSetting.s3_cdn_url = 'https://some.cdn.com' + describe "when cdn path is configured" do + before { SiteSetting.s3_cdn_url = "https://some.cdn.com" } - end - - it 'should return the right url' do - expect(helper.logo_url).to eq( - "https://some.cdn.com/original/1X/somesha1.png" - ) + it "should return the right url" do + expect(helper.logo_url).to eq("https://some.cdn.com/original/1X/somesha1.png") end end end diff --git a/spec/import_export/category_exporter_spec.rb b/spec/import_export/category_exporter_spec.rb index 5fda50252b9..4a41fe9e8b3 100644 --- a/spec/import_export/category_exporter_spec.rb +++ b/spec/import_export/category_exporter_spec.rb @@ -3,26 +3,23 @@ require "import_export" RSpec.describe ImportExport::CategoryExporter do - fab!(:category) { Fabricate(:category) } fab!(:group) { Fabricate(:group) } fab!(:user) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) } fab!(:user3) { Fabricate(:user) } - before do - STDOUT.stubs(:write) - end + before { STDOUT.stubs(:write) } - describe '.perform' do - it 'export the category when it is found' do + describe ".perform" do + it "export the category when it is found" do data = ImportExport::CategoryExporter.new([category.id]).perform.export_data expect(data[:categories].count).to eq(1) expect(data[:groups].count).to eq(0) end - it 'export the category with permission groups' do + it "export the category with permission groups" do _category_group = Fabricate(:category_group, category: category, group: group) data = ImportExport::CategoryExporter.new([category.id]).perform.export_data @@ -30,7 +27,7 @@ RSpec.describe ImportExport::CategoryExporter do expect(data[:groups].count).to eq(1) end - it 'export multiple categories' do + it "export multiple categories" do category2 = Fabricate(:category) _category_group = Fabricate(:category_group, category: category, group: group) data = ImportExport::CategoryExporter.new([category.id, category2.id]).perform.export_data @@ -39,7 +36,7 @@ RSpec.describe ImportExport::CategoryExporter do expect(data[:groups].count).to eq(1) end - it 'export the category with topics and users' do + it "export the category with topics and users" do topic1 = Fabricate(:topic, category: category, user_id: -1) Fabricate(:post, topic: topic1, user: User.find(-1), post_number: 1) topic2 = Fabricate(:topic, category: category, user: user) @@ -54,5 +51,4 @@ RSpec.describe ImportExport::CategoryExporter do expect(data[:users].map { |u| u[:id] }).to match_array([user.id, user2.id, user3.id]) end end - end diff --git a/spec/import_export/category_structure_exporter_spec.rb b/spec/import_export/category_structure_exporter_spec.rb index b5ee22fbdee..776ee20cd33 100644 --- a/spec/import_export/category_structure_exporter_spec.rb +++ b/spec/import_export/category_structure_exporter_spec.rb @@ -3,12 +3,9 @@ require "import_export/category_structure_exporter" RSpec.describe ImportExport::CategoryStructureExporter do + before { STDOUT.stubs(:write) } - before do - STDOUT.stubs(:write) - end - - it 'export all the categories' do + it "export all the categories" do category = Fabricate(:category) data = ImportExport::CategoryStructureExporter.new.perform.export_data @@ -17,7 +14,7 @@ RSpec.describe ImportExport::CategoryStructureExporter do expect(data[:users].blank?).to eq(true) end - it 'export all the categories with permission groups' do + it "export all the categories with permission groups" do category = Fabricate(:category) group = Fabricate(:group) category_group = Fabricate(:category_group, category: category, group: group) @@ -28,7 +25,7 @@ RSpec.describe ImportExport::CategoryStructureExporter do expect(data[:users].blank?).to eq(true) end - it 'export all the categories with permission groups and users' do + it "export all the categories with permission groups and users" do category = Fabricate(:category) group = Fabricate(:group) user = Fabricate(:user) @@ -40,5 +37,4 @@ RSpec.describe ImportExport::CategoryStructureExporter do expect(data[:groups].count).to eq(1) expect(data[:users].count).to eq(1) end - end diff --git a/spec/import_export/group_exporter_spec.rb b/spec/import_export/group_exporter_spec.rb index 8fd5c0f908a..d6e188b29af 100644 --- a/spec/import_export/group_exporter_spec.rb +++ b/spec/import_export/group_exporter_spec.rb @@ -3,12 +3,9 @@ require "import_export/group_exporter" RSpec.describe ImportExport::GroupExporter do + before { STDOUT.stubs(:write) } - before do - STDOUT.stubs(:write) - end - - it 'exports all the groups' do + it "exports all the groups" do group = Fabricate(:group) user = Fabricate(:user) group_user = Fabricate(:group_user, group: group, user: user) @@ -18,7 +15,7 @@ RSpec.describe ImportExport::GroupExporter do expect(data[:users].blank?).to eq(true) end - it 'exports all the groups with users' do + it "exports all the groups with users" do group = Fabricate(:group) user = Fabricate(:user) group_user = Fabricate(:group_user, group: group, user: user) @@ -27,5 +24,4 @@ RSpec.describe ImportExport::GroupExporter do expect(data[:groups].map { |g| g[:id] }).to include(group.id) expect(data[:users].map { |u| u[:id] }).to include(user.id) end - end diff --git a/spec/import_export/importer_spec.rb b/spec/import_export/importer_spec.rb index 39009ddabfb..00cc89203e6 100644 --- a/spec/import_export/importer_spec.rb +++ b/spec/import_export/importer_spec.rb @@ -3,9 +3,7 @@ require "import_export" RSpec.describe ImportExport::Importer do - before do - STDOUT.stubs(:write) - end + before { STDOUT.stubs(:write) } let(:import_data) do import_file = Rack::Test::UploadedFile.new(file_from_fixtures("import-export.json", "json")) @@ -16,93 +14,79 @@ RSpec.describe ImportExport::Importer do ImportExport::Importer.new(data).perform end - describe '.perform' do - it 'topics and users' do + describe ".perform" do + it "topics and users" do data = import_data.dup data[:categories] = nil data[:groups] = nil - expect { - import(data) - }.to not_change { Category.count } - .and not_change { Group.count } - .and change { Topic.count }.by(2) - .and change { User.count }.by(2) + expect { import(data) }.to not_change { Category.count }.and not_change { + Group.count + }.and change { Topic.count }.by(2).and change { User.count }.by(2) end - context 'with categories and groups' do - it 'works' do + context "with categories and groups" do + it "works" do data = import_data.dup data[:topics] = nil data[:users] = nil - expect { - import(data) - }.to change { Category.count }.by(6) - .and change { Group.count }.by(2) - .and change { Topic.count }.by(6) - .and not_change { User.count } + expect { import(data) }.to change { Category.count }.by(6).and change { Group.count }.by( + 2, + ).and change { Topic.count }.by(6).and not_change { User.count } end - it 'works with sub-sub-categories' do + it "works with sub-sub-categories" do data = import_data.dup # 11 -> 10 -> 15 data[:categories].find { |c| c[:id] == 10 }[:parent_category_id] = 11 data[:categories].find { |c| c[:id] == 15 }[:parent_category_id] = 10 - expect { import(data) } - .to change { Category.count }.by(6) - .and change { SiteSetting.max_category_nesting }.from(2).to(3) + expect { import(data) }.to change { Category.count }.by(6).and change { + SiteSetting.max_category_nesting + }.from(2).to(3) end - it 'fixes permissions' do + it "fixes permissions" do data = import_data.dup data[:categories].find { |c| c[:id] == 10 }[:permissions_params] = { custom_group: 1 } data[:categories].find { |c| c[:id] == 15 }[:permissions_params] = { staff: 1 } permissions = data[:categories].find { |c| c[:id] == 10 }[:permissions_params] - expect { import(data) } - .to change { Category.count }.by(6) - .and change { permissions[:staff] }.from(nil).to(1) + expect { import(data) }.to change { Category.count }.by(6).and change { + permissions[:staff] + }.from(nil).to(1) end end - it 'categories, groups and users' do + it "categories, groups and users" do data = import_data.dup data[:topics] = nil - expect { - import(data) - }.to change { Category.count }.by(6) - .and change { Group.count }.by(2) - .and change { Topic.count }.by(6) - .and change { User.count }.by(2) + expect { import(data) }.to change { Category.count }.by(6).and change { Group.count }.by( + 2, + ).and change { Topic.count }.by(6).and change { User.count }.by(2) end - it 'groups' do + it "groups" do data = import_data.dup data[:categories] = nil data[:topics] = nil data[:users] = nil - expect { - import(data) - }.to not_change { Category.count } - .and change { Group.count }.by(2) - .and not_change { Topic.count } - .and not_change { User.count } + expect { import(data) }.to not_change { Category.count }.and change { Group.count }.by( + 2, + ).and not_change { Topic.count }.and not_change { User.count } end - it 'all' do - expect { - import(import_data) - }.to change { Category.count }.by(6) - .and change { Group.count }.by(2) - .and change { Topic.count }.by(8) - .and change { User.count }.by(2) - .and change { TranslationOverride.count }.by(1) + it "all" do + expect { import(import_data) }.to change { Category.count }.by(6).and change { + Group.count + }.by(2).and change { Topic.count }.by(8).and change { User.count }.by(2).and change { + TranslationOverride.count + }.by(1) end end end diff --git a/spec/import_export/topic_exporter_spec.rb b/spec/import_export/topic_exporter_spec.rb index 809189158c3..a36318a5d39 100644 --- a/spec/import_export/topic_exporter_spec.rb +++ b/spec/import_export/topic_exporter_spec.rb @@ -3,17 +3,14 @@ require "import_export" RSpec.describe ImportExport::TopicExporter do - - before do - STDOUT.stubs(:write) - end + before { STDOUT.stubs(:write) } fab!(:user) { Fabricate(:user) } fab!(:topic) { Fabricate(:topic, user: user) } fab!(:post) { Fabricate(:post, topic: topic, user: user) } - describe '.perform' do - it 'export a single topic' do + describe ".perform" do + it "export a single topic" do data = ImportExport::TopicExporter.new([topic.id]).perform.export_data expect(data[:categories].blank?).to eq(true) @@ -22,7 +19,7 @@ RSpec.describe ImportExport::TopicExporter do expect(data[:users].count).to eq(1) end - it 'export multiple topics' do + it "export multiple topics" do topic2 = Fabricate(:topic, user: user) _post2 = Fabricate(:post, user: user, topic: topic2) data = ImportExport::TopicExporter.new([topic.id, topic2.id]).perform.export_data @@ -33,5 +30,4 @@ RSpec.describe ImportExport::TopicExporter do expect(data[:users].map { |u| u[:id] }).to match_array([user.id]) end end - end diff --git a/spec/initializers/track_setting_changes_spec.rb b/spec/initializers/track_setting_changes_spec.rb index bab78a1d334..47c538b4f5a 100644 --- a/spec/initializers/track_setting_changes_spec.rb +++ b/spec/initializers/track_setting_changes_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -RSpec.describe 'Setting changes' do - describe '#must_approve_users' do +RSpec.describe "Setting changes" do + describe "#must_approve_users" do before { SiteSetting.must_approve_users = false } - it 'does not approve a user with associated reviewables' do + it "does not approve a user with associated reviewables" do user_pending_approval = Fabricate(:reviewable_user).target SiteSetting.must_approve_users = true @@ -12,7 +12,7 @@ RSpec.describe 'Setting changes' do expect(user_pending_approval.reload.approved?).to eq(false) end - it 'approves a user with no associated reviewables' do + it "approves a user with no associated reviewables" do non_approved_user = Fabricate(:user, approved: false) SiteSetting.must_approve_users = true @@ -21,10 +21,10 @@ RSpec.describe 'Setting changes' do end end - describe '#reviewable_low_priority_threshold' do + describe "#reviewable_low_priority_threshold" do let(:new_threshold) { 5 } - it 'sets the low priority value' do + it "sets the low priority value" do medium_threshold = 10 Reviewable.set_priorities(medium: medium_threshold) diff --git a/spec/integration/api_keys_spec.rb b/spec/integration/api_keys_spec.rb index be9e790a681..c7ec616e202 100644 --- a/spec/integration/api_keys_spec.rb +++ b/spec/integration/api_keys_spec.rb @@ -1,25 +1,21 @@ # frozen_string_literal: true -RSpec.describe 'api keys' do +RSpec.describe "api keys" do let(:user) { Fabricate(:user) } let(:api_key) { ApiKey.create!(user_id: user.id, created_by_id: Discourse.system_user) } - it 'works in headers' do - get '/session/current.json', headers: { - HTTP_API_KEY: api_key.key - } + it "works in headers" do + get "/session/current.json", headers: { HTTP_API_KEY: api_key.key } expect(response.status).to eq(200) expect(response.parsed_body["current_user"]["username"]).to eq(user.username) end - it 'does not work in parameters' do - get '/session/current.json', params: { - api_key: api_key.key - } + it "does not work in parameters" do + get "/session/current.json", params: { api_key: api_key.key } expect(response.status).to eq(404) end - it 'allows parameters on ics routes' do + it "allows parameters on ics routes" do get "/u/#{user.username}/bookmarks.ics?api_key=#{api_key.key}&api_username=#{user.username.downcase}" expect(response.status).to eq(200) @@ -28,14 +24,14 @@ RSpec.describe 'api keys' do expect(response.status).to eq(403) end - it 'allows parameters for handle mail' do + it "allows parameters for handle mail" do admin_api_key = ApiKey.create!(user: Fabricate(:admin), created_by_id: Discourse.system_user) post "/admin/email/handle_mail.json?api_key=#{admin_api_key.key}", params: { email: "blah" } expect(response.status).to eq(200) end - it 'allows parameters for rss feeds' do + it "allows parameters for rss feeds" do SiteSetting.login_required = true get "/latest.rss?api_key=#{api_key.key}&api_username=#{user.username.downcase}" @@ -52,32 +48,28 @@ RSpec.describe 'api keys' do plugin.add_api_parameter_route methods: [:get], actions: ["session#current"] end - it 'allows parameter access to the registered route' do - get '/session/current.json', params: { - api_key: api_key.key - } + it "allows parameter access to the registered route" do + get "/session/current.json", params: { api_key: api_key.key } expect(response.status).to eq(200) end end end -RSpec.describe 'user api keys' do +RSpec.describe "user api keys" do let(:user) { Fabricate(:user) } let(:user_api_key) { Fabricate(:readonly_user_api_key, user: user) } - it 'updates last used time on use' do + it "updates last used time on use" do freeze_time user_api_key.update_columns(last_used_at: 7.days.ago) - get '/session/current.json', headers: { - HTTP_USER_API_KEY: user_api_key.key, - } + get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key } expect(user_api_key.reload.last_used_at).to eq_time(Time.zone.now) end - it 'allows parameters on ics routes' do + it "allows parameters on ics routes" do get "/u/#{user.username}/bookmarks.ics?user_api_key=#{user_api_key.key}" expect(response.status).to eq(200) @@ -86,7 +78,7 @@ RSpec.describe 'user api keys' do expect(response.status).to eq(403) end - it 'allows parameters for rss feeds' do + it "allows parameters for rss feeds" do SiteSetting.login_required = true get "/latest.rss?user_api_key=#{user_api_key.key}" @@ -102,27 +94,19 @@ RSpec.describe 'user api keys' do calendar_key = Fabricate(:bookmarks_calendar_user_api_key, user: admin) - get "/u/#{user.username}/bookmarks.json", headers: { - HTTP_USER_API_KEY: calendar_key.key, - } + get "/u/#{user.username}/bookmarks.json", headers: { HTTP_USER_API_KEY: calendar_key.key } expect(response.status).to eq(403) # Does not allow json - get "/u/#{user.username}/bookmarks.ics", headers: { - HTTP_USER_API_KEY: calendar_key.key, - } + get "/u/#{user.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key } expect(response.status).to eq(200) # Allows ICS # Now restrict the key calendar_key.scopes.first.update(allowed_parameters: { username: admin.username }) - get "/u/#{user.username}/bookmarks.ics", headers: { - HTTP_USER_API_KEY: calendar_key.key, - } + get "/u/#{user.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key } expect(response.status).to eq(403) # Cannot access another users calendar - get "/u/#{admin.username}/bookmarks.ics", headers: { - HTTP_USER_API_KEY: calendar_key.key, - } + get "/u/#{admin.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key } expect(response.status).to eq(200) # Can access own calendar end @@ -138,12 +122,9 @@ RSpec.describe 'user api keys' do user_api_key.save! end - it 'allows parameter access to the registered route' do - get '/session/current.json', headers: { - HTTP_USER_API_KEY: user_api_key.key - } + it "allows parameter access to the registered route" do + get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key } expect(response.status).to eq(200) end end - end diff --git a/spec/integration/auto_reject_reviewable_users_spec.rb b/spec/integration/auto_reject_reviewable_users_spec.rb index f993f097c21..dd09810733c 100644 --- a/spec/integration/auto_reject_reviewable_users_spec.rb +++ b/spec/integration/auto_reject_reviewable_users_spec.rb @@ -12,9 +12,7 @@ RSpec.describe "auto reject reviewable users" do Jobs::AutoQueueHandler.new.execute({}) expect(old_user.reload.rejected?).to eq(true) - expect(UserHistory.last.context).to eq( - I18n.t("user.destroy_reasons.reviewable_reject_auto") - ) + expect(UserHistory.last.context).to eq(I18n.t("user.destroy_reasons.reviewable_reject_auto")) end end end diff --git a/spec/integration/blocked_hotlinked_media_spec.rb b/spec/integration/blocked_hotlinked_media_spec.rb index c727210c48d..c9dfdc2a6ba 100644 --- a/spec/integration/blocked_hotlinked_media_spec.rb +++ b/spec/integration/blocked_hotlinked_media_spec.rb @@ -3,11 +3,20 @@ RSpec.describe "hotlinked media blocking" do let(:hotlinked_url) { "http://example.com/images/2/2e/Longcat1.png" } let(:onebox_url) { "http://example.com/onebox" } - let(:png) { Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") } + let(:png) do + Base64.decode64( + "R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==", + ) + end before do SiteSetting.download_remote_images_to_local = false - stub_request(:get, hotlinked_url).to_return(body: png, headers: { "Content-Type" => "image/png" }) + stub_request(:get, hotlinked_url).to_return( + body: png, + headers: { + "Content-Type" => "image/png", + }, + ) stub_image_size end @@ -19,38 +28,65 @@ RSpec.describe "hotlinked media blocking" do context "with hotlinked media blocked, before post-processing" do before do SiteSetting.block_hotlinked_media = true - Oneboxer.stubs(:cached_onebox).returns("") + Oneboxer.stubs(:cached_onebox).returns( + "", + ) end it "blocks hotlinked images" do post = Fabricate(:post, raw: "") expect(post.cooked).not_to have_tag("img[src]") - expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url }) + expect(post.cooked).to have_tag( + "img", + with: { + PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url, + }, + ) end it "blocks hotlinked videos with src" do post = Fabricate(:post, raw: "![alt text|video](#{hotlinked_url})") expect(post.cooked).not_to have_tag("video source[src]") - expect(post.cooked).to have_tag("video source", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url }) + expect(post.cooked).to have_tag( + "video source", + with: { + PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url, + }, + ) end it "blocks hotlinked videos with srcset" do srcset = "#{hotlinked_url} 1x,https://example.com 2x" post = Fabricate(:post, raw: "") expect(post.cooked).not_to have_tag("video source[srcset]") - expect(post.cooked).to have_tag("video source", with: { PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR => srcset }) + expect(post.cooked).to have_tag( + "video source", + with: { + PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR => srcset, + }, + ) end it "blocks hotlinked audio" do post = Fabricate(:post, raw: "![alt text|audio](#{hotlinked_url})") expect(post.cooked).not_to have_tag("audio source[src]") - expect(post.cooked).to have_tag("audio source", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url }) + expect(post.cooked).to have_tag( + "audio source", + with: { + PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url, + }, + ) end it "blocks hotlinked onebox content when cached (post_analyzer)" do post = Fabricate(:post, raw: "#{onebox_url}") expect(post.cooked).not_to have_tag("img[src]") - expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url }) + expect(post.cooked).to have_tag( + "img", + with: { + PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url, + }, + ) end it "allows relative URLs" do @@ -81,8 +117,18 @@ RSpec.describe "hotlinked media blocking" do post.reload expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com" }) expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com/myimage.png" }) - expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://example.com.malicious.com/myimage.png" }) - expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://malicious.invalid/https://example.com" }) + expect(post.cooked).to have_tag( + "img", + with: { + PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://example.com.malicious.com/myimage.png", + }, + ) + expect(post.cooked).to have_tag( + "img", + with: { + PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://malicious.invalid/https://example.com", + }, + ) end it "allows multiple exceptions" do @@ -126,7 +172,7 @@ RSpec.describe "hotlinked media blocking" do expect(post.cooked).not_to have_tag("audio") expect(post.cooked).to have_tag( "a.blocked-hotlinked-placeholder[href^='http://example.com'][rel='noopener nofollow ugc']", - count: 4 + count: 4, ) end end diff --git a/spec/integration/category_tag_spec.rb b/spec/integration/category_tag_spec.rb index 59bee8e8975..7a67d1928b7 100644 --- a/spec/integration/category_tag_spec.rb +++ b/spec/integration/category_tag_spec.rb @@ -6,13 +6,13 @@ RSpec.describe "category tag restrictions" do DiscourseTagging.filter_allowed_tags(Guardian.new(user), opts) end - fab!(:tag1) { Fabricate(:tag, name: 'tag1') } - fab!(:tag2) { Fabricate(:tag, name: 'tag2') } - fab!(:tag3) { Fabricate(:tag, name: 'tag3') } - fab!(:tag4) { Fabricate(:tag, name: 'tag4') } - let(:tag_with_colon) { Fabricate(:tag, name: 'with:colon') } + fab!(:tag1) { Fabricate(:tag, name: "tag1") } + fab!(:tag2) { Fabricate(:tag, name: "tag2") } + fab!(:tag3) { Fabricate(:tag, name: "tag3") } + fab!(:tag4) { Fabricate(:tag, name: "tag4") } + let(:tag_with_colon) { Fabricate(:tag, name: "with:colon") } - fab!(:user) { Fabricate(:user) } + fab!(:user) { Fabricate(:user) } fab!(:admin) { Fabricate(:admin) } before do @@ -23,29 +23,29 @@ RSpec.describe "category tag restrictions" do context "with tags restricted to one category" do fab!(:category_with_tags) { Fabricate(:category) } - fab!(:other_category) { Fabricate(:category) } + fab!(:other_category) { Fabricate(:category) } - before do - category_with_tags.tags = [tag1, tag2] - end + before { category_with_tags.tags = [tag1, tag2] } it "tags belonging to that category can only be used there" do - msg = I18n.t( - "tags.forbidden.category_does_not_allow_tags", - count: 1, - tags: tag3.name, - category: category_with_tags.name - ) + msg = + I18n.t( + "tags.forbidden.category_does_not_allow_tags", + count: 1, + tags: tag3.name, + category: category_with_tags.name, + ) expect { create_post(category: category_with_tags, tags: [tag1.name, tag2.name, tag3.name]) }.to raise_error(StandardError, msg) - msg = I18n.t( - "tags.forbidden.restricted_tags_cannot_be_used_in_category", - count: 2, - tags: [tag1, tag2].map(&:name).sort.join(", "), - category: other_category.name - ) + msg = + I18n.t( + "tags.forbidden.restricted_tags_cannot_be_used_in_category", + count: 2, + tags: [tag1, tag2].map(&:name).sort.join(", "), + category: other_category.name, + ) expect { create_post(category: other_category, tags: [tag1.name, tag2.name, tag3.name]) }.to raise_error(StandardError, msg) @@ -53,27 +53,60 @@ RSpec.describe "category tag restrictions" do it "search can show only permitted tags" do expect(filter_allowed_tags.count).to eq(Tag.count) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag2]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category_with_tags), + [tag1, tag2], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name], term: 'tag'), [tag2]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4]) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag1.name], + ), + [tag2], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag1.name], + term: "tag", + ), + [tag2], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), + [tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: other_category, + selected_tags: [tag3.name], + term: "tag", + ), + [tag4], + ) end it "search can handle colons in tag names" do tag_with_colon - expect_same_tag_names(filter_allowed_tags(for_input: true, term: 'with:c'), [tag_with_colon]) + expect_same_tag_names(filter_allowed_tags(for_input: true, term: "with:c"), [tag_with_colon]) end it "can't create new tags in a restricted category" do - msg = I18n.t( - "tags.forbidden.category_does_not_allow_tags", - count: 1, - tags: "newtag", - category: category_with_tags.name - ) + msg = + I18n.t( + "tags.forbidden.category_does_not_allow_tags", + count: 1, + tags: "newtag", + category: category_with_tags.name, + ) expect { create_post(category: category_with_tags, tags: [tag1.name, "newtag"]) }.to raise_error(StandardError, msg) @@ -89,61 +122,161 @@ RSpec.describe "category tag restrictions" do end it "can create tags when changing category settings" do - expect { other_category.update(allowed_tags: ['newtag']) }.to change { Tag.count }.by(1) - expect { other_category.update(allowed_tags: [tag1.name, 'tag-stuff', tag2.name, 'another-tag']) }.to change { Tag.count }.by(2) + expect { other_category.update(allowed_tags: ["newtag"]) }.to change { Tag.count }.by(1) + expect { + other_category.update(allowed_tags: [tag1.name, "tag-stuff", tag2.name, "another-tag"]) + }.to change { Tag.count }.by(2) end - context 'with required tags from tag group' do + context "with required tags from tag group" do fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) } - before { category_with_tags.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) } + before do + category_with_tags.update!( + category_required_tag_groups: [ + CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1), + ], + ) + end it "search only returns the allowed tags" do - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag2.name]), [tag1]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category_with_tags), + [tag1], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag1.name], + ), + [tag2], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag2.name], + ), + [tag1], + ) end end - context 'when category allows other tags to be used' do - before do - category_with_tags.update!(allow_global_tags: true) - end + context "when category allows other tags to be used" do + before { category_with_tags.update!(allow_global_tags: true) } it "search can show the permitted tags" do expect(filter_allowed_tags.count).to eq(Tag.count) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag2, tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category_with_tags), + [tag1, tag2, tag3, tag4], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name], term: 'tag'), [tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4]) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag1.name], + ), + [tag2, tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag1.name], + term: "tag", + ), + [tag2, tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: other_category, + selected_tags: [tag3.name], + ), + [tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: other_category, + selected_tags: [tag3.name], + term: "tag", + ), + [tag4], + ) end it "works if no tags are restricted to the category" do other_category.update!(allow_global_tags: true) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: other_category, + selected_tags: [tag3.name], + ), + [tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: other_category, + selected_tags: [tag3.name], + term: "tag", + ), + [tag4], + ) end - context 'with required tags from tag group' do + context "with required tags from tag group" do fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) } - before { category_with_tags.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) } + before do + category_with_tags.update!( + category_required_tag_groups: [ + CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1), + ], + ) + end it "search only returns the allowed tags" do - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag3]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag2.name]), [tag1, tag3]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category_with_tags), + [tag1, tag3], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag1.name], + ), + [tag2, tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category_with_tags, + selected_tags: [tag2.name], + ), + [tag1, tag3], + ) end end end end context "with tag groups restricted to a category" do - fab!(:tag_group1) { Fabricate(:tag_group) } - fab!(:category) { Fabricate(:category) } - fab!(:other_category) { Fabricate(:category) } + fab!(:tag_group1) { Fabricate(:tag_group) } + fab!(:category) { Fabricate(:category) } + fab!(:other_category) { Fabricate(:category) } before do tag_group1.tags = [tag1, tag2] @@ -156,7 +289,10 @@ RSpec.describe "category tag restrictions" do expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4]) tag_group1.tags = [tag2, tag3, tag4] - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag2, tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag2, tag3, tag4], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1]) expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1]) end @@ -165,74 +301,122 @@ RSpec.describe "category tag restrictions" do category.allowed_tags = [tag4.name] category.reload - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag2, tag4], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3]) expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3]) end it "enforces restrictions when creating a topic" do - msg = I18n.t( - "tags.forbidden.category_does_not_allow_tags", - count: 1, - tags: "newtag", - category: category.name + msg = + I18n.t( + "tags.forbidden.category_does_not_allow_tags", + count: 1, + tags: "newtag", + category: category.name, + ) + expect { create_post(category: category, tags: [tag1.name, "newtag"]) }.to raise_error( + StandardError, + msg, ) - expect { - create_post(category: category, tags: [tag1.name, "newtag"]) - }.to raise_error(StandardError, msg) end it "handles colons" do tag_with_colon - expect_same_tag_names(filter_allowed_tags(for_input: true, term: 'with:c'), [tag_with_colon]) + expect_same_tag_names(filter_allowed_tags(for_input: true, term: "with:c"), [tag_with_colon]) end - context 'with required tags from tag group' do + context "with required tags from tag group" do fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) } - before { category.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) } + before do + category.update!( + category_required_tag_groups: [ + CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1), + ], + ) + end it "search only returns the allowed tags" do expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), + [tag2], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), + [tag1], + ) end end - context 'when category allows other tags to be used' do - before do - category.update!(allow_global_tags: true) - end + context "when category allows other tags to be used" do + before { category.update!(allow_global_tags: true) } - it 'filters tags correctly' do - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag3, tag4]) + it "filters tags correctly" do + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag2, tag3, tag4], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag3, tag4], + ) tag_group1.tags = [tag2, tag3, tag4] - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag2, tag3, tag4], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag1], + ) end it "works if no tags are restricted to the category" do other_category.update!(allow_global_tags: true) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag3, tag4], + ) tag_group1.tags = [tag2, tag3, tag4] - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag1], + ) end - context 'with required tags from tag group' do + context "with required tags from tag group" do fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) } - before { category.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) } + before do + category.update!( + category_required_tag_groups: [ + CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1), + ], + ) + end it "search only returns the allowed tags" do - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag3]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1, tag3]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag3], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), + [tag2, tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), + [tag1, tag3], + ) end end - context 'when another category has restricted tags using groups' do + context "when another category has restricted tags using groups" do fab!(:category2) { Fabricate(:category) } fab!(:tag_group2) { Fabricate(:tag_group) } @@ -242,32 +426,59 @@ RSpec.describe "category tag restrictions" do category2.reload end - it 'filters tags correctly' do - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3]) + it "filters tags correctly" do + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category2), + [tag2, tag3], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag2, tag4], + ) end it "doesn't care about tags in a group that isn't used in a category" do unused_tag_group = Fabricate(:tag_group) unused_tag_group.tags = [tag4] - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category2), + [tag2, tag3], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag2, tag4], + ) end end - context 'when another category has restricted tags' do + context "when another category has restricted tags" do fab!(:category2) { Fabricate(:category) } it "doesn't filter tags that are also restricted in another category" do category2.tags = [tag2, tag3] - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category2), + [tag2, tag3], + ) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag2, tag4], + ) end end end @@ -278,80 +489,139 @@ RSpec.describe "category tag restrictions" do tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id) tag_group.tags = [tag3, tag4] expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name, tag3.name]), [tag2, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), + [tag2, tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag1.name, tag3.name]), + [tag2, tag4], + ) end it "for tagging a topic, filter_allowed_tags allows tags without parent tag" do tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id) tag_group.tags = [tag3, tag4] expect_same_tag_names(filter_allowed_tags(for_topic: true), [tag1, tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_topic: true, selected_tags: [tag1.name]), [tag1, tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_topic: true, selected_tags: [tag1.name, tag3.name]), [tag1, tag2, tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_topic: true, selected_tags: [tag1.name]), + [tag1, tag2, tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags(for_topic: true, selected_tags: [tag1.name, tag3.name]), + [tag1, tag2, tag3, tag4], + ) end it "filter_allowed_tags returns tags common to more than one tag group with parent tag" do - common = Fabricate(:tag, name: 'common') + common = Fabricate(:tag, name: "common") tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id) tag_group.tags = [tag2, common] tag_group = Fabricate(:tag_group, parent_tag_id: tag3.id) tag_group.tags = [tag4] expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), + [tag2, tag3, common], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), + [tag4, tag1], + ) tag_group.tags = [tag4, common] expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1, common]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), + [tag2, tag3, common], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), + [tag4, tag1, common], + ) parent_tag_group = Fabricate(:tag_group, tags: [tag1, tag3]) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1, common]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), + [tag2, tag3, common], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), + [tag4, tag1, common], + ) parent_tag_group.update!(one_per_topic: true) expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, common]) - expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, common]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), + [tag2, common], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), + [tag4, common], + ) end - context 'with required tags from tag group' do + context "with required tags from tag group" do fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag2]) } - fab!(:category) { Fabricate(:category, category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) } + fab!(:category) do + Fabricate( + :category, + category_required_tag_groups: [ + CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1), + ], + ) + end it "search only returns the allowed tags" do tag_group_with_parent = Fabricate(:tag_group, parent_tag_id: tag1.id, tags: [tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name, tag2.name]), [tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category), + [tag1, tag2], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), + [tag1], + ) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), + [tag2, tag3, tag4], + ) + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: category, + selected_tags: [tag1.name, tag2.name], + ), + [tag3, tag4], + ) end end context "with category restrictions" do - fab!(:car_category) { Fabricate(:category) } - fab!(:other_category) { Fabricate(:category) } - fab!(:makes) { Fabricate(:tag_group, name: "Makes") } - fab!(:honda_group) { Fabricate(:tag_group, name: "Honda Models") } - fab!(:ford_group) { Fabricate(:tag_group, name: "Ford Models") } + fab!(:car_category) { Fabricate(:category) } + fab!(:other_category) { Fabricate(:category) } + fab!(:makes) { Fabricate(:tag_group, name: "Makes") } + fab!(:honda_group) { Fabricate(:tag_group, name: "Honda Models") } + fab!(:ford_group) { Fabricate(:tag_group, name: "Ford Models") } before do @tags = {} - ['honda', 'ford', 'civic', 'accord', 'mustang', 'taurus'].each do |name| + %w[honda ford civic accord mustang taurus].each do |name| @tags[name] = Fabricate(:tag, name: name) end - makes.tags = [@tags['honda'], @tags['ford']] + makes.tags = [@tags["honda"], @tags["ford"]] - honda_group.parent_tag_id = @tags['honda'].id + honda_group.parent_tag_id = @tags["honda"].id honda_group.save - honda_group.tags = [@tags['civic'], @tags['accord']] + honda_group.tags = [@tags["civic"], @tags["accord"]] - ford_group.parent_tag_id = @tags['ford'].id + ford_group.parent_tag_id = @tags["ford"].id ford_group.save - ford_group.tags = [@tags['mustang'], @tags['taurus']] + ford_group.tags = [@tags["mustang"], @tags["taurus"]] car_category.allowed_tag_groups = [makes.name, honda_group.name, ford_group.name] end @@ -359,39 +629,75 @@ RSpec.describe "category tag restrictions" do it "handles all those rules" do # car tags can't be used outside of car category: expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2, tag3, tag4]) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1, tag2, tag3, tag4]) + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: other_category), + [tag1, tag2, tag3, tag4], + ) # in car category, a make tag must be given first: - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category))).to eq(['ford', 'honda']) + expect( + sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category)), + ).to eq(%w[ford honda]) # model tags depend on which make is chosen: - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic', 'ford']) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['honda', 'mustang', 'taurus']) + expect( + sorted_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]), + ), + ).to eq(%w[accord civic ford]) + expect( + sorted_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]), + ), + ).to eq(%w[honda mustang taurus]) makes.update!(one_per_topic: true) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic']) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus']) + expect( + sorted_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]), + ), + ).to eq(%w[accord civic]) + expect( + sorted_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]), + ), + ).to eq(%w[mustang taurus]) honda_group.update!(one_per_topic: true) ford_group.update!(one_per_topic: true) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic']) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus']) + expect( + sorted_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]), + ), + ).to eq(%w[accord civic]) + expect( + sorted_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]), + ), + ).to eq(%w[mustang taurus]) car_category.update!(allow_global_tags: true) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category), - ['ford', 'honda', tag1, tag2, tag3, tag4] + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: car_category), + ["ford", "honda", tag1, tag2, tag3, tag4], ) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']), - ['mustang', 'taurus', tag1, tag2, tag3, tag4] + expect_same_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]), + ["mustang", "taurus", tag1, tag2, tag3, tag4], ) - expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford', 'mustang']), - [tag1, tag2, tag3, tag4] + expect_same_tag_names( + filter_allowed_tags( + for_input: true, + category: car_category, + selected_tags: %w[ford mustang], + ), + [tag1, tag2, tag3, tag4], ) end it "can apply the tags to a topic" do - post = create_post(category: car_category, tags: ['ford', 'mustang']) - expect(post.topic.tags.map(&:name).sort).to eq(['ford', 'mustang']) + post = create_post(category: car_category, tags: %w[ford mustang]) + expect(post.topic.tags.map(&:name).sort).to eq(%w[ford mustang]) end context "with limit one tag from each group" do @@ -402,24 +708,51 @@ RSpec.describe "category tag restrictions" do end it "can restrict one tag from each group" do - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category))).to eq(['ford', 'honda']) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic']) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus']) - expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford', 'mustang']))).to eq([]) + expect( + sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category)), + ).to eq(%w[ford honda]) + expect( + sorted_tag_names( + filter_allowed_tags( + for_input: true, + category: car_category, + selected_tags: ["honda"], + ), + ), + ).to eq(%w[accord civic]) + expect( + sorted_tag_names( + filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]), + ), + ).to eq(%w[mustang taurus]) + expect( + sorted_tag_names( + filter_allowed_tags( + for_input: true, + category: car_category, + selected_tags: %w[ford mustang], + ), + ), + ).to eq([]) end it "can apply the tags to a topic" do - post = create_post(category: car_category, tags: ['ford', 'mustang']) - expect(post.topic.tags.map(&:name).sort).to eq(['ford', 'mustang']) + post = create_post(category: car_category, tags: %w[ford mustang]) + expect(post.topic.tags.map(&:name).sort).to eq(%w[ford mustang]) end it "can remove extra tags from the same group" do # A weird case that input field wouldn't allow. # Only one tag from car makers is allowed, but we're saying that two have been selected. - names = filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda', 'ford']).map(&:name) - expect(names.include?('honda') || names.include?('ford')).to eq(false) - expect(names).to include('civic') - expect(names).to include('mustang') + names = + filter_allowed_tags( + for_input: true, + category: car_category, + selected_tags: %w[honda ford], + ).map(&:name) + expect(names.include?("honda") || names.include?("ford")).to eq(false) + expect(names).to include("civic") + expect(names).to include("mustang") end end end @@ -441,9 +774,9 @@ RSpec.describe "tag topic counts per category" do end it "counts when a topic is created with tags" do - expect { - Fabricate(:topic, category: category, tags: [tag1, tag2]) - }.to change { CategoryTagStat.count }.by(2) + expect { Fabricate(:topic, category: category, tags: [tag1, tag2]) }.to change { + CategoryTagStat.count + }.by(2) expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(1) expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(1) end @@ -461,7 +794,7 @@ RSpec.describe "tag topic counts per category" do context "with topic with 2 tags" do fab!(:topic) { Fabricate(:topic, category: category, tags: [tag1, tag2]) } - fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) } + fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) } it "has correct counts after tag is removed from a topic" do post @@ -473,7 +806,12 @@ RSpec.describe "tag topic counts per category" do end it "has correct counts after a topic's category changes" do - PostRevisor.new(post).revise!(topic.user, category_id: category2.id, raw: post.raw, tags: [tag1.name, tag2.name]) + PostRevisor.new(post).revise!( + topic.user, + category_id: category2.id, + raw: post.raw, + tags: [tag1.name, tag2.name], + ) expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0) expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0) expect(CategoryTagStat.where(category: category2, tag: tag1).sum(:topic_count)).to eq(1) @@ -481,7 +819,12 @@ RSpec.describe "tag topic counts per category" do end it "has correct counts after topic's category AND tags changed" do - PostRevisor.new(post).revise!(topic.user, raw: post.raw, tags: [tag2.name, tag3.name], category_id: category2.id) + PostRevisor.new(post).revise!( + topic.user, + raw: post.raw, + tags: [tag2.name, tag3.name], + category_id: category2.id, + ) expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0) expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0) expect(CategoryTagStat.where(category: category, tag: tag3).sum(:topic_count)).to eq(0) @@ -496,8 +839,18 @@ RSpec.describe "tag topic counts per category" do fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) } it "counts after topic becomes uncategorized" do - PostRevisor.new(post).revise!(topic.user, raw: post.raw, tags: [tag1.name], category_id: SiteSetting.uncategorized_category_id) - expect(CategoryTagStat.where(category: Category.find(SiteSetting.uncategorized_category_id), tag: tag1).sum(:topic_count)).to eq(1) + PostRevisor.new(post).revise!( + topic.user, + raw: post.raw, + tags: [tag1.name], + category_id: SiteSetting.uncategorized_category_id, + ) + expect( + CategoryTagStat.where( + category: Category.find(SiteSetting.uncategorized_category_id), + tag: tag1, + ).sum(:topic_count), + ).to eq(1) expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0) end diff --git a/spec/integration/content_security_policy_spec.rb b/spec/integration/content_security_policy_spec.rb index 69a56acc65b..658a50bb7a6 100644 --- a/spec/integration/content_security_policy_spec.rb +++ b/spec/integration/content_security_policy_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true -RSpec.describe 'content security policy integration' do - +RSpec.describe "content security policy integration" do it "adds the csp headers correctly" do SiteSetting.content_security_policy = false get "/" @@ -15,8 +14,10 @@ RSpec.describe 'content security policy integration' do context "with different hostnames" do before do SiteSetting.content_security_policy = true - RailsMultisite::ConnectionManagement.stubs(:current_db_hostnames).returns(['primary.example.com', 'secondary.example.com']) - RailsMultisite::ConnectionManagement.stubs(:current_hostname).returns('primary.example.com') + RailsMultisite::ConnectionManagement.stubs(:current_db_hostnames).returns( + %w[primary.example.com secondary.example.com], + ) + RailsMultisite::ConnectionManagement.stubs(:current_hostname).returns("primary.example.com") end it "works with the primary domain" do @@ -52,5 +53,4 @@ RSpec.describe 'content security policy integration' do expect(response.headers["Content-Security-Policy"]).to include("https://test.localhost") end end - end diff --git a/spec/integration/discord_omniauth_spec.rb b/spec/integration/discord_omniauth_spec.rb index 0ca80d1c96c..230459a8850 100644 --- a/spec/integration/discord_omniauth_spec.rb +++ b/spec/integration/discord_omniauth_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe 'Discord OAuth2' do +describe "Discord OAuth2" do let(:access_token) { "discord_access_token_448" } let(:client_id) { "abcdef11223344" } let(:client_secret) { "adddcccdddd99922" } @@ -9,15 +9,14 @@ describe 'Discord OAuth2' do fab!(:user1) { Fabricate(:user) } def setup_discord_email_stub(email, verified:) - stub_request(:get, "https://discord.com/api/users/@me") - .with( - headers: { - "Authorization" => "Bearer #{access_token}" - } - ) - .to_return( - status: 200, - body: JSON.dump( + stub_request(:get, "https://discord.com/api/users/@me").with( + headers: { + "Authorization" => "Bearer #{access_token}", + }, + ).to_return( + status: 200, + body: + JSON.dump( id: "80351110224678912", username: "Nelly", discriminator: "1337", @@ -26,14 +25,14 @@ describe 'Discord OAuth2' do email: email, flags: 64, banner: "06c16474723fe537c283b8efa61a30c8", - accent_color: 16711680, + accent_color: 16_711_680, premium_type: 1, - public_flags: 64 + public_flags: 64, ), - headers: { - "Content-Type" => "application/json" - } - ) + headers: { + "Content-Type" => "application/json", + }, + ) end before do @@ -41,50 +40,49 @@ describe 'Discord OAuth2' do SiteSetting.discord_client_id = client_id SiteSetting.discord_secret = client_secret - stub_request(:post, "https://discord.com/api/oauth2/token") - .with( - body: hash_including( + stub_request(:post, "https://discord.com/api/oauth2/token").with( + body: + hash_including( "client_id" => client_id, "client_secret" => client_secret, "code" => temp_code, "grant_type" => "authorization_code", - "redirect_uri" => "http://test.localhost/auth/discord/callback" - ) - ) - .to_return( - status: 200, - body: Rack::Utils.build_query( + "redirect_uri" => "http://test.localhost/auth/discord/callback", + ), + ).to_return( + status: 200, + body: + Rack::Utils.build_query( access_token: access_token, scope: "identify emails guilds", token_type: "Bearer", - expires_in: 604800, + expires_in: 604_800, refresh_token: "D43f5y0ahjqew82jZ4NViEr2YafMKhue", ), - headers: { - "Content-Type" => "application/x-www-form-urlencoded" - } - ) + headers: { + "Content-Type" => "application/x-www-form-urlencoded", + }, + ) - stub_request(:get, "https://discord.com/api/users/@me/guilds") - .with( - headers: { - "Authorization" => "Bearer #{access_token}" - } - ) - .to_return( - status: 200, - body: JSON.dump( + stub_request(:get, "https://discord.com/api/users/@me/guilds").with( + headers: { + "Authorization" => "Bearer #{access_token}", + }, + ).to_return( + status: 200, + body: + JSON.dump( id: "80351110224678912", name: "1337 Krew", icon: "8342729096ea3675442027381ff50dfe", owner: true, permissions: "36953089", - features: ["COMMUNITY", "NEWS"] + features: %w[COMMUNITY NEWS], ), - headers: { - "Content-Type" => "application/json" - } - ) + headers: { + "Content-Type" => "application/json", + }, + ) end it "doesn't sign in anyone if the email from discord is not verified" do @@ -94,10 +92,7 @@ describe 'Discord OAuth2' do setup_discord_email_stub(user1.email, verified: false) - post "/auth/discord/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") @@ -111,10 +106,7 @@ describe 'Discord OAuth2' do setup_discord_email_stub(user1.email, verified: true) - post "/auth/discord/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") diff --git a/spec/integration/email_style_spec.rb b/spec/integration/email_style_spec.rb index a53cff48e22..1b6aa37a7da 100644 --- a/spec/integration/email_style_spec.rb +++ b/spec/integration/email_style_spec.rb @@ -6,14 +6,14 @@ RSpec.describe EmailStyle do SiteSetting.email_custom_template = "%{email_content}<%= (111 * 333) %>" html = Email::Renderer.new(UserNotifications.signup(Fabricate(:user))).html expect(html).not_to include("36963") - expect(html).to include('') + expect(html).to include("") end end context "with a custom template" do before do SiteSetting.email_custom_template = "

FOR YOU

%{email_content}
" - SiteSetting.email_custom_css = 'h1 { color: red; } div.body { color: #FAB; }' + SiteSetting.email_custom_css = "h1 { color: red; } div.body { color: #FAB; }" SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css end @@ -22,36 +22,36 @@ RSpec.describe EmailStyle do SiteSetting.remove_override!(:email_custom_css) end - context 'with invite' do + context "with invite" do fab!(:invite) { Fabricate(:invite) } let(:invite_mail) { InviteMailer.send_invite(invite) } subject(:mail_html) { Email::Renderer.new(invite_mail).html } - it 'applies customizations' do + it "applies customizations" do expect(mail_html.scan('

FOR YOU

').count).to eq(1) expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") end - it 'applies customizations if compiled is missing' do + it "applies customizations if compiled is missing" do SiteSetting.remove_override!(:email_custom_css_compiled) expect(mail_html.scan('

FOR YOU

').count).to eq(1) expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") end - it 'can apply RTL attrs' do - SiteSetting.default_locale = 'he' + it "can apply RTL attrs" do + SiteSetting.default_locale = "he" body_attrs = mail_html.match(/])+/) expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/) expect(body_attrs[0]&.downcase).to include('dir="rtl"') end end - context 'when user_replied' do + context "when user_replied" do let(:response_by_user) { Fabricate(:user, name: "John Doe") } - let(:category) { Fabricate(:category, name: 'India') } + let(:category) { Fabricate(:category, name: "India") } let(:topic) { Fabricate(:topic, category: category, title: "Super cool topic") } - let(:post) { Fabricate(:post, topic: topic, raw: 'This is My super duper cool topic') } + let(:post) { Fabricate(:post, topic: topic, raw: "This is My super duper cool topic") } let(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) } let(:user) { Fabricate(:user) } let(:notification) { Fabricate(:replied_notification, user: user, post: response) } @@ -61,7 +61,7 @@ RSpec.describe EmailStyle do user, post: response, notification_type: notification.notification_type, - notification_data_hash: notification.data_hash + notification_data_hash: notification.data_hash, ) end @@ -72,42 +72,45 @@ RSpec.describe EmailStyle do expect(mail_html.scan('

FOR YOU

').count).to eq(1) matches = mail_html.match(/
#{post.raw}/) - expect(matches[1]).to include('color: #FAB;') # custom - expect(matches[1]).to include('padding-top:5px;') # div.body + expect(matches[1]).to include("color: #FAB;") # custom + expect(matches[1]).to include("padding-top:5px;") # div.body end # TODO: translation override end - context 'with signup' do + context "with signup" do let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) } subject(:mail_html) { Email::Renderer.new(signup_mail).html } it "customizations are applied to html part of emails" do expect(mail_html.scan('

FOR YOU

').count).to eq(1) - expect(mail_html).to include('activate-account') + expect(mail_html).to include("activate-account") end - context 'with translation override' do + context "with translation override" do before do TranslationOverride.upsert!( SiteSetting.default_locale, - 'user_notifications.signup.text_body_template', - "CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}" + "user_notifications.signup.text_body_template", + "CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}", ) end after do - TranslationOverride.revert!(SiteSetting.default_locale, ['user_notifications.signup.text_body_template']) + TranslationOverride.revert!( + SiteSetting.default_locale, + ["user_notifications.signup.text_body_template"], + ) end it "applies customizations when translation override exists" do expect(mail_html.scan('

FOR YOU

').count).to eq(1) - expect(mail_html.scan('CLICK THAT LINK').count).to eq(1) + expect(mail_html.scan("CLICK THAT LINK").count).to eq(1) end end - context 'with some bad css' do + context "with some bad css" do before do SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; ' SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css @@ -115,13 +118,15 @@ RSpec.describe EmailStyle do it "can render the html" do expect(mail_html.scan(/FOR YOU<\/h1>/).count).to eq(1) - expect(mail_html).to include('activate-account') + expect(mail_html).to include("activate-account") end end end - context 'with digest' do - fab!(:popular_topic) { Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) } + context "with digest" do + fab!(:popular_topic) do + Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) + end let(:summary_email) { UserNotifications.digest(Fabricate(:user)) } subject(:mail_html) { Email::Renderer.new(summary_email).html } @@ -133,7 +138,7 @@ RSpec.describe EmailStyle do it "doesn't apply customizations if apply_custom_styles_to_digest is disabled" do SiteSetting.apply_custom_styles_to_digest = false expect(mail_html).to_not include('

FOR YOU

') - expect(mail_html).to_not include('FOR YOU') + expect(mail_html).to_not include("FOR YOU") expect(mail_html).to include(popular_topic.title) end end diff --git a/spec/integration/facebook_omniauth_spec.rb b/spec/integration/facebook_omniauth_spec.rb index e1bef766680..5f596af6eec 100644 --- a/spec/integration/facebook_omniauth_spec.rb +++ b/spec/integration/facebook_omniauth_spec.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true -describe 'Facebook OAuth2' do +describe "Facebook OAuth2" do let(:access_token) { "facebook_access_token_448" } let(:app_id) { "432489234823984" } let(:app_secret) { "adddcccdddd99922" } let(:temp_code) { "facebook_temp_code_544254" } - let(:appsecret_proof) { OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, app_secret, access_token) } + let(:appsecret_proof) do + OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, app_secret, access_token) + end fab!(:user1) { Fabricate(:user) } @@ -18,19 +20,16 @@ describe 'Facebook OAuth2' do } body[:email] = email if email - stub_request(:get, "https://graph.facebook.com/v5.0/me?appsecret_proof=#{appsecret_proof}&fields=name,first_name,last_name,email") - .with( - headers: { - "Authorization" => "OAuth #{access_token}" - } - ) - .to_return( - status: 200, - body: JSON.dump(body), - headers: { - "Content-Type" => "application/json" - } - ) + stub_request( + :get, + "https://graph.facebook.com/v5.0/me?appsecret_proof=#{appsecret_proof}&fields=name,first_name,last_name,email", + ).with(headers: { "Authorization" => "OAuth #{access_token}" }).to_return( + status: 200, + body: JSON.dump(body), + headers: { + "Content-Type" => "application/json", + }, + ) end before do @@ -38,27 +37,23 @@ describe 'Facebook OAuth2' do SiteSetting.facebook_app_id = app_id SiteSetting.facebook_app_secret = app_secret - stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token") - .with( - body: hash_including( + stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token").with( + body: + hash_including( "client_id" => app_id, "client_secret" => app_secret, "code" => temp_code, "grant_type" => "authorization_code", - "redirect_uri" => "http://test.localhost/auth/facebook/callback" - ) - ) - .to_return( - status: 200, - body: Rack::Utils.build_query( - access_token: access_token, - scope: "email", - token_type: "Bearer", + "redirect_uri" => "http://test.localhost/auth/facebook/callback", ), - headers: { - "Content-Type" => "application/x-www-form-urlencoded" - } - ) + ).to_return( + status: 200, + body: + Rack::Utils.build_query(access_token: access_token, scope: "email", token_type: "Bearer"), + headers: { + "Content-Type" => "application/x-www-form-urlencoded", + }, + ) end it "signs in the user if the API response from facebook includes an email (implies it's verified) and the email matches an existing user's" do @@ -68,10 +63,7 @@ describe 'Facebook OAuth2' do setup_facebook_email_stub(email: user1.email) - post "/auth/facebook/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") @@ -85,10 +77,7 @@ describe 'Facebook OAuth2' do setup_facebook_email_stub(email: nil) - post "/auth/facebook/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") diff --git a/spec/integration/flags_spec.rb b/spec/integration/flags_spec.rb index 74591747577..61201ca49d5 100644 --- a/spec/integration/flags_spec.rb +++ b/spec/integration/flags_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true RSpec.describe PostAction do - it "triggers the 'flag_reviewed' event when there was at least one flag" do admin = Fabricate(:admin) @@ -14,5 +13,4 @@ RSpec.describe PostAction do events = DiscourseEvent.track_events { PostDestroyer.new(admin, flagged_post).destroy } expect(events.map { |e| e[:event_name] }).to include(:flag_reviewed) end - end diff --git a/spec/integration/github_omniauth_spec.rb b/spec/integration/github_omniauth_spec.rb index 9714526d1b4..05cbcdddc48 100644 --- a/spec/integration/github_omniauth_spec.rb +++ b/spec/integration/github_omniauth_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe 'GitHub Oauth2' do +describe "GitHub Oauth2" do let(:access_token) { "github_access_token_448" } let(:client_id) { "abcdef11223344" } let(:client_secret) { "adddcccdddd99922" } @@ -10,19 +10,17 @@ describe 'GitHub Oauth2' do fab!(:user2) { Fabricate(:user) } def setup_github_emails_stub(emails) - stub_request(:get, "https://api.github.com/user/emails") - .with( - headers: { - "Authorization" => "Bearer #{access_token}" - } - ) - .to_return( - status: 200, - body: JSON.dump(emails), - headers: { - "Content-Type" => "application/json" - } - ) + stub_request(:get, "https://api.github.com/user/emails").with( + headers: { + "Authorization" => "Bearer #{access_token}", + }, + ).to_return( + status: 200, + body: JSON.dump(emails), + headers: { + "Content-Type" => "application/json", + }, + ) end before do @@ -30,35 +28,34 @@ describe 'GitHub Oauth2' do SiteSetting.github_client_id = client_id SiteSetting.github_client_secret = client_secret - stub_request(:post, "https://github.com/login/oauth/access_token") - .with( - body: hash_including( + stub_request(:post, "https://github.com/login/oauth/access_token").with( + body: + hash_including( "client_id" => client_id, "client_secret" => client_secret, "code" => temp_code, - ) - ) - .to_return( - status: 200, - body: Rack::Utils.build_query( + ), + ).to_return( + status: 200, + body: + Rack::Utils.build_query( access_token: access_token, scope: "user:email", - token_type: "bearer" + token_type: "bearer", ), - headers: { - "Content-Type" => "application/x-www-form-urlencoded" - } - ) + headers: { + "Content-Type" => "application/x-www-form-urlencoded", + }, + ) - stub_request(:get, "https://api.github.com/user") - .with( - headers: { - "Authorization" => "Bearer #{access_token}" - } - ) - .to_return( - status: 200, - body: JSON.dump( + stub_request(:get, "https://api.github.com/user").with( + headers: { + "Authorization" => "Bearer #{access_token}", + }, + ).to_return( + status: 200, + body: + JSON.dump( login: "octocat", id: 1, node_id: "MDQ6VXNlcjE=", @@ -94,20 +91,20 @@ describe 'GitHub Oauth2' do private_gists: 81, total_private_repos: 100, owned_private_repos: 100, - disk_usage: 10000, + disk_usage: 10_000, collaborators: 8, two_factor_authentication: true, plan: { name: "Medium", space: 400, private_repos: 20, - collaborators: 0 - } + collaborators: 0, + }, ), - headers: { - "Content-Type" => "application/json" - } - ) + headers: { + "Content-Type" => "application/json", + }, + ) end it "doesn't sign in anyone if none of the emails from github are verified" do @@ -117,25 +114,12 @@ describe 'GitHub Oauth2' do setup_github_emails_stub( [ - { - email: user1.email, - primary: true, - verified: false, - visibility: "private" - }, - { - email: user2.email, - primary: false, - verified: false, - visibility: "private" - } - ] + { email: user1.email, primary: true, verified: false, visibility: "private" }, + { email: user2.email, primary: false, verified: false, visibility: "private" }, + ], ) - post "/auth/github/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") expect(session[:current_user_id]).to be_blank @@ -148,25 +132,12 @@ describe 'GitHub Oauth2' do setup_github_emails_stub( [ - { - email: user1.email, - primary: true, - verified: false, - visibility: "private" - }, - { - email: user2.email, - primary: false, - verified: true, - visibility: "private" - } - ] + { email: user1.email, primary: true, verified: false, visibility: "private" }, + { email: user2.email, primary: false, verified: true, visibility: "private" }, + ], ) - post "/auth/github/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") expect(session[:current_user_id]).to eq(user2.id) @@ -183,21 +154,13 @@ describe 'GitHub Oauth2' do email: "somerandomemail@discourse.org", primary: true, verified: true, - visibility: "private" + visibility: "private", }, - { - email: user2.email, - primary: false, - verified: false, - visibility: "private" - } - ] + { email: user2.email, primary: false, verified: false, visibility: "private" }, + ], ) - post "/auth/github/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") expect(session[:current_user_id]).to be_blank @@ -210,25 +173,12 @@ describe 'GitHub Oauth2' do setup_github_emails_stub( [ - { - email: user1.email, - primary: true, - verified: true, - visibility: "private" - }, - { - email: user2.email, - primary: false, - verified: true, - visibility: "private" - } - ] + { email: user1.email, primary: true, verified: true, visibility: "private" }, + { email: user2.email, primary: false, verified: true, visibility: "private" }, + ], ) - post "/auth/github/callback", params: { - state: session["omniauth.state"], - code: temp_code - } + post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code } expect(response.status).to eq(302) expect(response.location).to eq("http://test.localhost/") expect(session[:current_user_id]).to eq(user1.id) diff --git a/spec/integration/group_spec.rb b/spec/integration/group_spec.rb index 0573053b68c..c9bf71e2068 100644 --- a/spec/integration/group_spec.rb +++ b/spec/integration/group_spec.rb @@ -6,18 +6,20 @@ RSpec.describe Group do :group, visibility_level: Group.visibility_levels[:public], mentionable_level: Group::ALIAS_LEVELS[:nobody], - users: [ Fabricate(:user) ] + users: [Fabricate(:user)], ) end let(:post) { Fabricate(:post, raw: "mention @#{group.name}") } - before do - Jobs.run_immediately! - end + before { Jobs.run_immediately! } - it 'users can mention public groups, but does not create a notification' do - expect { post }.not_to change { Notification.where(notification_type: Notification.types[:group_mentioned]).count } - expect(post.cooked).to include("@#{group.name}") + it "users can mention public groups, but does not create a notification" do + expect { post }.not_to change { + Notification.where(notification_type: Notification.types[:group_mentioned]).count + } + expect(post.cooked).to include( + "@#{group.name}", + ) end end diff --git a/spec/integration/invalid_request_spec.rb b/spec/integration/invalid_request_spec.rb index e3e4e0bad77..6d19bcb9c68 100644 --- a/spec/integration/invalid_request_spec.rb +++ b/spec/integration/invalid_request_spec.rb @@ -1,27 +1,31 @@ # frozen_string_literal: true -RSpec.describe 'invalid requests', type: :request do +RSpec.describe "invalid requests", type: :request do before do @orig_logger = Rails.logger Rails.logger = @fake_logger = FakeLogger.new end - after do - Rails.logger = @orig_logger - end + after { Rails.logger = @orig_logger } it "handles NotFound with invalid json body" do - post "/latest.json", params: "{some: malformed: json", headers: { "content-type" => "application/json" } + post "/latest.json", + params: "{some: malformed: json", + headers: { + "content-type" => "application/json", + } expect(response.status).to eq(404) expect(@fake_logger.warnings.length).to eq(0) expect(@fake_logger.errors.length).to eq(0) end it "handles EOFError when multipart request is malformed" do - post "/latest.json", params: "somecontent", headers: { - "content-type" => "multipart/form-data; boundary=abcde", - "content-length" => "1" - } + post "/latest.json", + params: "somecontent", + headers: { + "content-type" => "multipart/form-data; boundary=abcde", + "content-length" => "1", + } expect(response.status).to eq(400) expect(@fake_logger.warnings.length).to eq(0) expect(@fake_logger.errors.length).to eq(0) @@ -33,5 +37,4 @@ RSpec.describe 'invalid requests', type: :request do expect(@fake_logger.warnings.length).to eq(0) expect(@fake_logger.errors.length).to eq(0) end - end diff --git a/spec/integration/invite_only_registration_spec.rb b/spec/integration/invite_only_registration_spec.rb index a30869094ea..1ea4a9c1eca 100644 --- a/spec/integration/invite_only_registration_spec.rb +++ b/spec/integration/invite_only_registration_spec.rb @@ -1,45 +1,46 @@ # encoding: UTF-8 # frozen_string_literal: true -RSpec.describe 'invite only' do - - describe '#create invite only' do - it 'can create user via API' do - +RSpec.describe "invite only" do + describe "#create invite only" do + it "can create user via API" do SiteSetting.invite_only = true Jobs.run_immediately! admin = Fabricate(:admin) api_key = Fabricate(:api_key, user: admin) - post '/users.json', params: { - name: 'bob', - username: 'bob', - password: 'strongpassword', - email: 'bob@bob.com', - }, headers: { - HTTP_API_KEY: api_key.key, - HTTP_API_USERNAME: admin.username - } + post "/users.json", + params: { + name: "bob", + username: "bob", + password: "strongpassword", + email: "bob@bob.com", + }, + headers: { + HTTP_API_KEY: api_key.key, + HTTP_API_USERNAME: admin.username, + } user_id = response.parsed_body["user_id"] expect(user_id).to be > 0 # activate and approve - put "/admin/users/#{user_id}/activate.json", headers: { - HTTP_API_KEY: api_key.key, - HTTP_API_USERNAME: admin.username - } + put "/admin/users/#{user_id}/activate.json", + headers: { + HTTP_API_KEY: api_key.key, + HTTP_API_USERNAME: admin.username, + } - put "/admin/users/#{user_id}/approve.json", headers: { - HTTP_API_KEY: api_key.key, - HTTP_API_USERNAME: admin.username - } + put "/admin/users/#{user_id}/approve.json", + headers: { + HTTP_API_KEY: api_key.key, + HTTP_API_USERNAME: admin.username, + } u = User.find(user_id) expect(u.active).to eq(true) expect(u.approved).to eq(true) - end end end diff --git a/spec/integration/message_bus_spec.rb b/spec/integration/message_bus_spec.rb index a2f961ddc55..ca814fa8fff 100644 --- a/spec/integration/message_bus_spec.rb +++ b/spec/integration/message_bus_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true -RSpec.describe 'message bus integration' do - +RSpec.describe "message bus integration" do it "allows anonymous requests to the messagebus" do post "/message-bus/poll" expect(response.status).to eq(200) @@ -27,5 +26,4 @@ RSpec.describe 'message bus integration' do expect(response.status).to eq(200) end end - end diff --git a/spec/integration/multisite_cookies_spec.rb b/spec/integration/multisite_cookies_spec.rb index 15bad1c74bb..9a24b4d0e2d 100644 --- a/spec/integration/multisite_cookies_spec.rb +++ b/spec/integration/multisite_cookies_spec.rb @@ -1,19 +1,25 @@ # frozen_string_literal: true -RSpec.describe 'multisite', type: [:multisite, :request] do +RSpec.describe "multisite", type: %i[multisite request] do it "works" do get "http://test.localhost/session/csrf.json" expect(response.status).to eq(200) cookie = CGI.escape(response.cookies["_forum_session"]) id1 = session["session_id"] - get "http://test.localhost/session/csrf.json", headers: { "Cookie" => "_forum_session=#{cookie};" } + get "http://test.localhost/session/csrf.json", + headers: { + "Cookie" => "_forum_session=#{cookie};", + } expect(response.status).to eq(200) id2 = session["session_id"] expect(id1).to eq(id2) - get "http://test2.localhost/session/csrf.json", headers: { "Cookie" => "_forum_session=#{cookie};" } + get "http://test2.localhost/session/csrf.json", + headers: { + "Cookie" => "_forum_session=#{cookie};", + } expect(response.status).to eq(200) id3 = session["session_id"] diff --git a/spec/integration/multisite_spec.rb b/spec/integration/multisite_spec.rb index d53c4435606..a1f27013f6d 100644 --- a/spec/integration/multisite_spec.rb +++ b/spec/integration/multisite_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe 'multisite', type: [:multisite, :request] do +RSpec.describe "multisite", type: %i[multisite request] do it "should always allow /srv/status through" do get "http://unknown.com/srv/status" expect(response.status).to eq(200) @@ -13,10 +13,12 @@ RSpec.describe 'multisite', type: [:multisite, :request] do end it "should hit correct site otherwise" do - site_1_url = Fabricate(:topic, title: "Site 1 Topic Title", user: Discourse.system_user).relative_url + site_1_url = + Fabricate(:topic, title: "Site 1 Topic Title", user: Discourse.system_user).relative_url - test_multisite_connection('second') do - site_2_url = Fabricate(:topic, title: "Site 2 Topic Title", user: Discourse.system_user).relative_url + test_multisite_connection("second") do + site_2_url = + Fabricate(:topic, title: "Site 2 Topic Title", user: Discourse.system_user).relative_url get "http://test.localhost/#{site_1_url}.json" expect(request.env["RAILS_MULTISITE_HOST"]).to eq("test.localhost") diff --git a/spec/integration/rate_limiting_spec.rb b/spec/integration/rate_limiting_spec.rb index ebf24e909b3..cdda0a8a75b 100644 --- a/spec/integration/rate_limiting_spec.rb +++ b/spec/integration/rate_limiting_spec.rb @@ -1,8 +1,7 @@ # encoding: UTF-8 # frozen_string_literal: true -RSpec.describe 'rate limiter integration' do - +RSpec.describe "rate limiter integration" do before do RateLimiter.enable RateLimiter.clear_all! @@ -13,12 +12,13 @@ RSpec.describe 'rate limiter integration' do global_setting :reject_message_bus_queue_seconds, 0.1 - post "/message-bus/#{SecureRandom.hex}/poll", headers: { - "HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.2}" - } + post "/message-bus/#{SecureRandom.hex}/poll", + headers: { + "HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.2}", + } expect(response.status).to eq(429) - expect(response.headers['Retry-After'].to_i).to be > 29 + expect(response.headers["Retry-After"].to_i).to be > 29 end it "will not rate limit when all is good" do @@ -26,9 +26,10 @@ RSpec.describe 'rate limiter integration' do global_setting :reject_message_bus_queue_seconds, 0.1 - post "/message-bus/#{SecureRandom.hex}/poll", headers: { - "HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.05}" - } + post "/message-bus/#{SecureRandom.hex}/poll", + headers: { + "HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.05}", + } expect(response.status).to eq(200) end @@ -37,15 +38,15 @@ RSpec.describe 'rate limiter integration' do name = Auth::DefaultCurrentUserProvider::TOKEN_COOKIE # we try 11 times because the rate limit is 10 - 11.times { + 11.times do cookies[name] = SecureRandom.hex - get '/categories.json' + get "/categories.json" expect(response.cookies.has_key?(name)).to eq(true) expect(response.cookies[name]).to be_nil - } + end end - it 'can cleanly limit requests and sets a Retry-After header' do + it "can cleanly limit requests and sets a Retry-After header" do freeze_time RateLimiter.clear_all! @@ -55,17 +56,19 @@ RSpec.describe 'rate limiter integration' do global_setting :max_admin_api_reqs_per_minute, 1 - get '/admin/api/keys.json', headers: { - HTTP_API_KEY: api_key.key, - HTTP_API_USERNAME: admin.username - } + get "/admin/api/keys.json", + headers: { + HTTP_API_KEY: api_key.key, + HTTP_API_USERNAME: admin.username, + } expect(response.status).to eq(200) - get '/admin/api/keys.json', headers: { - HTTP_API_KEY: api_key.key, - HTTP_API_USERNAME: admin.username - } + get "/admin/api/keys.json", + headers: { + HTTP_API_KEY: api_key.key, + HTTP_API_USERNAME: admin.username, + } expect(response.status).to eq(429) diff --git a/spec/integration/request_tracker_spec.rb b/spec/integration/request_tracker_spec.rb index 239d4155d3d..bec86c245b5 100644 --- a/spec/integration/request_tracker_spec.rb +++ b/spec/integration/request_tracker_spec.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true -RSpec.describe 'request tracker' do +RSpec.describe "request tracker" do let(:api_key) do Fabricate( :api_key, user: Fabricate.build(:user), - api_key_scopes: [ApiKeyScope.new(resource: 'users', action: 'show')] + api_key_scopes: [ApiKeyScope.new(resource: "users", action: "show")], ) end let(:user_api_key) do - Fabricate(:user_api_key, scopes: [Fabricate.build(:user_api_key_scope, name: 'session_info')]) + Fabricate(:user_api_key, scopes: [Fabricate.build(:user_api_key_scope, name: "session_info")]) end before do @@ -25,8 +25,8 @@ RSpec.describe 'request tracker' do CachedCounting.disable end - context 'when using an api key' do - it 'is counted as an API request' do + context "when using an api key" do + it "is counted as an API request" do get "/u/#{api_key.user.username}.json", headers: { HTTP_API_KEY: api_key.key } expect(response.status).to eq(200) @@ -37,9 +37,9 @@ RSpec.describe 'request tracker' do end end - context 'when using an user api key' do - it 'is counted as a user API request' do - get '/session/current.json', headers: { HTTP_USER_API_KEY: user_api_key.key } + context "when using an user api key" do + it "is counted as a user API request" do + get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key } expect(response.status).to eq(200) CachedCounting.flush diff --git a/spec/integration/same_ip_spammers_spec.rb b/spec/integration/same_ip_spammers_spec.rb index a586891cf44..2d3f9289208 100644 --- a/spec/integration/same_ip_spammers_spec.rb +++ b/spec/integration/same_ip_spammers_spec.rb @@ -2,45 +2,41 @@ # frozen_string_literal: true RSpec.describe "spammers on same IP" do - let(:ip_address) { '182.189.119.174' } - let!(:spammer1) { Fabricate(:user, ip_address: ip_address) } - let!(:spammer2) { Fabricate(:user, ip_address: ip_address) } - let(:spammer3) { Fabricate(:user, ip_address: ip_address) } + let(:ip_address) { "182.189.119.174" } + let!(:spammer1) { Fabricate(:user, ip_address: ip_address) } + let!(:spammer2) { Fabricate(:user, ip_address: ip_address) } + let(:spammer3) { Fabricate(:user, ip_address: ip_address) } - context 'when flag_sockpuppets is disabled' do - let!(:first_post) { create_post(user: spammer1) } - let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) } + context "when flag_sockpuppets is disabled" do + let!(:first_post) { create_post(user: spammer1) } + let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) } - it 'should not increase spam count' do - expect(first_post.reload.spam_count).to eq(0) + it "should not increase spam count" do + expect(first_post.reload.spam_count).to eq(0) expect(second_post.reload.spam_count).to eq(0) end end - context 'when flag_sockpuppets is enabled' do - before do - SiteSetting.flag_sockpuppets = true - end + context "when flag_sockpuppets is enabled" do + before { SiteSetting.flag_sockpuppets = true } - after do - SiteSetting.flag_sockpuppets = false - end + after { SiteSetting.flag_sockpuppets = false } - context 'when first spammer starts a topic' do + context "when first spammer starts a topic" do let!(:first_post) { create_post(user: spammer1) } - context 'when second spammer replies' do - let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) } + context "when second spammer replies" do + let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) } - it 'should increase spam count' do + it "should increase spam count" do expect(first_post.reload.spam_count).to eq(1) expect(second_post.reload.spam_count).to eq(1) end - context 'with third spam post' do + context "with third spam post" do let!(:third_post) { create_post(user: spammer3, topic: first_post.topic) } - it 'should increase spam count' do + it "should increase spam count" do expect(first_post.reload.spam_count).to eq(1) expect(second_post.reload.spam_count).to eq(1) expect(third_post.reload.spam_count).to eq(1) @@ -49,16 +45,18 @@ RSpec.describe "spammers on same IP" do end end - context 'when first user is not new' do - let!(:old_user) { Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago, trust_level: TrustLevel[1]) } + context "when first user is not new" do + let!(:old_user) do + Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago, trust_level: TrustLevel[1]) + end - context 'when first user starts a topic' do + context "when first user starts a topic" do let!(:first_post) { create_post(user: old_user) } - context 'with a reply by a new user at the same IP address' do - let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) } + context "with a reply by a new user at the same IP address" do + let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) } - it 'should increase the spam count correctly' do + it "should increase the spam count correctly" do expect(first_post.reload.spam_count).to eq(0) expect(second_post.reload.spam_count).to eq(1) end diff --git a/spec/integration/spam_rules_spec.rb b/spec/integration/spam_rules_spec.rb index edefab13de0..9c1f2c9f78d 100644 --- a/spec/integration/spam_rules_spec.rb +++ b/spec/integration/spam_rules_spec.rb @@ -2,11 +2,11 @@ # frozen_string_literal: true RSpec.describe "spam rules for users" do - describe 'auto-silence users based on flagging' do - fab!(:admin) { Fabricate(:admin) } # needed to send a system message + describe "auto-silence users based on flagging" do + fab!(:admin) { Fabricate(:admin) } # needed to send a system message fab!(:moderator) { Fabricate(:moderator) } - fab!(:user1) { Fabricate(:user) } - fab!(:user2) { Fabricate(:user) } + fab!(:user1) { Fabricate(:user) } + fab!(:user2) { Fabricate(:user) } before do SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:disabled] @@ -15,23 +15,21 @@ RSpec.describe "spam rules for users" do SiteSetting.num_users_to_silence_new_user = 2 end - context 'when spammer is a new user' do - fab!(:spammer) { Fabricate(:user, trust_level: TrustLevel[0]) } + context "when spammer is a new user" do + fab!(:spammer) { Fabricate(:user, trust_level: TrustLevel[0]) } - context 'when spammer post is not flagged enough times' do - let!(:spam_post) { create_post(user: spammer) } + context "when spammer post is not flagged enough times" do + let!(:spam_post) { create_post(user: spammer) } let!(:spam_post2) { create_post(user: spammer) } - before do - PostActionCreator.create(user1, spam_post, :spam) - end + before { PostActionCreator.create(user1, spam_post, :spam) } - it 'should not hide the post' do + it "should not hide the post" do expect(spam_post.reload).to_not be_hidden end - context 'when spam posts are flagged enough times, but not by enough users' do - it 'should not hide the post' do + context "when spam posts are flagged enough times, but not by enough users" do + it "should not hide the post" do PostActionCreator.create(user1, spam_post2, :spam) expect(spam_post.reload).to_not be_hidden @@ -40,16 +38,30 @@ RSpec.describe "spam rules for users" do end end - context 'when one spam post is flagged enough times by enough users' do + context "when one spam post is flagged enough times by enough users" do fab!(:another_topic) { Fabricate(:topic) } let!(:private_messages_count) { spammer.private_topics_count } let!(:mod_pm_count) { moderator.private_topics_count } let!(:reviewable) { PostActionCreator.spam(user2, spam_post).reviewable } - it 'should hide the posts' do + it "should hide the posts" do expect(Guardian.new(spammer).can_create_topic?(nil)).to be(false) - expect { PostCreator.create(spammer, title: 'limited time offer for you', raw: 'better buy this stuff ok', archetype_id: 1) }.to raise_error(Discourse::InvalidAccess) - expect(PostCreator.create(spammer, topic_id: another_topic.id, raw: 'my reply is spam in your topic', archetype_id: 1)).to eq(nil) + expect { + PostCreator.create( + spammer, + title: "limited time offer for you", + raw: "better buy this stuff ok", + archetype_id: 1, + ) + }.to raise_error(Discourse::InvalidAccess) + expect( + PostCreator.create( + spammer, + topic_id: another_topic.id, + raw: "my reply is spam in your topic", + archetype_id: 1, + ), + ).to eq(nil) expect(spammer.reload).to be_silenced expect(spam_post.reload).to be_hidden expect(spam_post2.reload).to be_hidden @@ -57,22 +69,24 @@ RSpec.describe "spam rules for users" do end context "when a post is deleted" do - it 'should silence the spammer' do - spam_post.trash!(moderator); spammer.reload + it "should silence the spammer" do + spam_post.trash!(moderator) + spammer.reload expect(spammer.reload).to be_silenced end end context "when spammer becomes trust level 1" do - it 'should silence the spammer' do - spammer.change_trust_level!(TrustLevel[1]); spammer.reload + it "should silence the spammer" do + spammer.change_trust_level!(TrustLevel[1]) + spammer.reload expect(spammer.reload).to be_silenced end end end - context 'with hide_post_sensitivity' do - it 'should silence the spammer' do + context "with hide_post_sensitivity" do + it "should silence the spammer" do Reviewable.set_priorities(high: 2.0) SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low] PostActionCreator.create(user2, spam_post, :spam) @@ -84,19 +98,26 @@ RSpec.describe "spam rules for users" do end context "when spammer has trust level basic" do - let(:spammer) { Fabricate(:user, trust_level: TrustLevel[1]) } + let(:spammer) { Fabricate(:user, trust_level: TrustLevel[1]) } - context 'when one spam post is flagged enough times by enough users' do - let!(:spam_post) { Fabricate(:post, user: spammer) } + context "when one spam post is flagged enough times by enough users" do + let!(:spam_post) { Fabricate(:post, user: spammer) } let!(:private_messages_count) { spammer.private_topics_count } - it 'should not allow spammer to create new posts' do + it "should not allow spammer to create new posts" do PostActionCreator.create(user1, spam_post, :spam) PostActionCreator.create(user2, spam_post, :spam) expect(spam_post.reload).to_not be_hidden expect(Guardian.new(spammer).can_create_topic?(nil)).to be(true) - expect { PostCreator.create(spammer, title: 'limited time offer for you', raw: 'better buy this stuff ok', archetype_id: 1) }.to_not raise_error + expect { + PostCreator.create( + spammer, + title: "limited time offer for you", + raw: "better buy this stuff ok", + archetype_id: 1, + ) + }.to_not raise_error expect(spammer.reload.private_topics_count).to eq(private_messages_count) end end @@ -104,11 +125,11 @@ RSpec.describe "spam rules for users" do [[:user, trust_level: TrustLevel[2]], [:admin], [:moderator]].each do |spammer_args| context "spammer is trusted #{spammer_args[0]}" do - let!(:spammer) { Fabricate(*spammer_args) } - let!(:spam_post) { Fabricate(:post, user: spammer) } + let!(:spammer) { Fabricate(*spammer_args) } + let!(:spam_post) { Fabricate(:post, user: spammer) } let!(:private_messages_count) { spammer.private_topics_count } - it 'should not hide the post' do + it "should not hide the post" do PostActionCreator.create(user1, spam_post, :spam) PostActionCreator.create(user2, spam_post, :spam) diff --git a/spec/integration/topic_auto_close_spec.rb b/spec/integration/topic_auto_close_spec.rb index 95821bc30ed..d4daaa070a6 100644 --- a/spec/integration/topic_auto_close_spec.rb +++ b/spec/integration/topic_auto_close_spec.rb @@ -4,36 +4,34 @@ RSpec.describe Topic do let(:job_klass) { Jobs::CloseTopic } - context 'when creating a topic without auto-close' do + context "when creating a topic without auto-close" do let(:topic) { Fabricate(:topic, category: category) } - context 'when uncategorized' do + context "when uncategorized" do let(:category) { nil } - it 'should not schedule the topic to auto-close' do + it "should not schedule the topic to auto-close" do expect(topic.public_topic_timer).to eq(nil) expect(job_klass.jobs).to eq([]) end end - context 'with category without default auto-close' do + context "with category without default auto-close" do let(:category) { Fabricate(:category, auto_close_hours: nil) } - it 'should not schedule the topic to auto-close' do + it "should not schedule the topic to auto-close" do expect(topic.public_topic_timer).to eq(nil) expect(job_klass.jobs).to eq([]) end end - context 'when jobs may be queued' do - before do - freeze_time - end + context "when jobs may be queued" do + before { freeze_time } - context 'when category has a default auto-close' do + context "when category has a default auto-close" do let(:category) { Fabricate(:category, auto_close_hours: 2.0) } - it 'should schedule the topic to auto-close' do + it "should schedule the topic to auto-close" do topic topic_status_update = TopicTimer.last @@ -42,11 +40,11 @@ RSpec.describe Topic do expect(topic.public_topic_timer.execute_at).to be_within_one_second_of(2.hours.from_now) end - context 'when topic was created by staff user' do + context "when topic was created by staff user" do let(:admin) { Fabricate(:admin) } let(:staff_topic) { Fabricate(:topic, user: admin, category: category) } - it 'should schedule the topic to auto-close' do + it "should schedule the topic to auto-close" do staff_topic topic_status_update = TopicTimer.last @@ -56,23 +54,24 @@ RSpec.describe Topic do expect(topic_status_update.user).to eq(Discourse.system_user) end - context 'when topic is closed manually' do - it 'should remove the schedule to auto-close the topic' do + context "when topic is closed manually" do + it "should remove the schedule to auto-close the topic" do topic_timer_id = staff_topic.public_topic_timer.id - staff_topic.update_status('closed', true, admin) + staff_topic.update_status("closed", true, admin) - expect(TopicTimer.with_deleted.find(topic_timer_id).deleted_at) - .to be_within_one_second_of(Time.zone.now) + expect( + TopicTimer.with_deleted.find(topic_timer_id).deleted_at, + ).to be_within_one_second_of(Time.zone.now) end end end - context 'when topic was created by a non-staff user' do + context "when topic was created by a non-staff user" do let(:regular_user) { Fabricate(:user) } let(:regular_user_topic) { Fabricate(:topic, user: regular_user, category: category) } - it 'should schedule the topic to auto-close' do + it "should schedule the topic to auto-close" do regular_user_topic topic_status_update = TopicTimer.last diff --git a/spec/integration/topic_thumbnail_spec.rb b/spec/integration/topic_thumbnail_spec.rb index 9a51836c5a6..26743940977 100644 --- a/spec/integration/topic_thumbnail_spec.rb +++ b/spec/integration/topic_thumbnail_spec.rb @@ -9,11 +9,11 @@ RSpec.describe "Topic Thumbnails" do fab!(:topic) { Fabricate(:topic, image_upload_id: image.id) } fab!(:user) { Fabricate(:user) } - describe 'latest' do + describe "latest" do def get_topic Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes)) Discourse.redis.del(topic.thumbnail_job_redis_key([])) - get '/latest.json' + get "/latest.json" expect(response.status).to eq(200) response.parsed_body["topic_list"]["topics"][0] end @@ -27,11 +27,7 @@ RSpec.describe "Topic Thumbnails" do context "with a theme" do before do theme = Fabricate(:theme) - theme.theme_modifier_set.topic_thumbnail_sizes = [ - [10, 10], - [20, 20], - [30, 30] - ] + theme.theme_modifier_set.topic_thumbnail_sizes = [[10, 10], [20, 20], [30, 30]] theme.theme_modifier_set.save! theme.set_default! end @@ -39,15 +35,15 @@ RSpec.describe "Topic Thumbnails" do it "includes the theme specified resolutions" do topic_json = nil - expect do - topic_json = get_topic - end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(2) + expect do topic_json = get_topic end.to change { + Jobs::GenerateTopicThumbnails.jobs.size + }.by(2) - expect( - Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] } - ).to eq([ - nil, # Job for core/plugin sizes - [[10, 10], [20, 20], [30, 30]]] # Job for theme sizes + expect(Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] }).to eq( + [ + nil, # Job for core/plugin sizes + [[10, 10], [20, 20], [30, 30]], + ], # Job for theme sizes ) thumbnails = topic_json["thumbnails"] @@ -67,9 +63,9 @@ RSpec.describe "Topic Thumbnails" do Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access) # Request again - expect do - topic_json = get_topic - end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size } + expect do topic_json = get_topic end.not_to change { + Jobs::GenerateTopicThumbnails.jobs.size + } thumbnails = topic_json["thumbnails"] @@ -82,7 +78,6 @@ RSpec.describe "Topic Thumbnails" do expect(thumbnails[1]["width"]).to eq(9) expect(thumbnails[1]["height"]).to eq(9) expect(thumbnails[1]["url"]).to include("/optimized/") - end end @@ -92,25 +87,23 @@ RSpec.describe "Topic Thumbnails" do plugin.register_topic_thumbnail_size [512, 512] end - after do - DiscoursePluginRegistry.reset! - end + after { DiscoursePluginRegistry.reset! } it "includes the theme specified resolutions" do topic_json = nil - expect do - topic_json = get_topic - end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(1) + expect do topic_json = get_topic end.to change { + Jobs::GenerateTopicThumbnails.jobs.size + }.by(1) # Run the job args = Jobs::GenerateTopicThumbnails.jobs.last["args"].first Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access) # Request again - expect do - topic_json = get_topic - end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size } + expect do topic_json = get_topic end.not_to change { + Jobs::GenerateTopicThumbnails.jobs.size + } thumbnails = topic_json["thumbnails"] diff --git a/spec/integration/twitter_omniauth_spec.rb b/spec/integration/twitter_omniauth_spec.rb index 4e4e54793ed..f64960c6e97 100644 --- a/spec/integration/twitter_omniauth_spec.rb +++ b/spec/integration/twitter_omniauth_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe 'Twitter OAuth 1.0a' do +describe "Twitter OAuth 1.0a" do let(:access_token) { "twitter_access_token_448" } let(:consumer_key) { "abcdef11223344" } let(:consumer_secret) { "adddcccdddd99922" } @@ -14,14 +14,15 @@ describe 'Twitter OAuth 1.0a' do created_at: "Sat May 09 17:58:22 +0000 2009", default_profile: false, default_profile_image: false, - description: "I taught your phone that thing you like. The Mobile Partner Engineer @Twitter. ", + description: + "I taught your phone that thing you like. The Mobile Partner Engineer @Twitter. ", favourites_count: 588, follow_request_sent: nil, - followers_count: 10625, + followers_count: 10_625, following: nil, friends_count: 1181, geo_enabled: true, - id: 38895958, + id: 38_895_958, id_str: "38895958", is_translator: false, lang: "en", @@ -30,11 +31,14 @@ describe 'Twitter OAuth 1.0a' do name: "Sean Cook", notifications: nil, profile_background_color: "1A1B1F", - profile_background_image_url: "http://a0.twimg.com/profile_background_images/495742332/purty_wood.png", - profile_background_image_url_https: "https://si0.twimg.com/profile_background_images/495742332/purty_wood.png", + profile_background_image_url: + "http://a0.twimg.com/profile_background_images/495742332/purty_wood.png", + profile_background_image_url_https: + "https://si0.twimg.com/profile_background_images/495742332/purty_wood.png", profile_background_tile: true, profile_image_url: "http://a0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG", - profile_image_url_https: "https://si0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG", + profile_image_url_https: + "https://si0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG", profile_link_color: "2FC2EF", profile_sidebar_border_color: "181A1E", profile_sidebar_fill_color: "252429", @@ -46,19 +50,17 @@ describe 'Twitter OAuth 1.0a' do statuses_count: 2609, time_zone: "Pacific Time (US & Canada)", url: nil, - utc_offset: -28800, + utc_offset: -28_800, verified: true, - email: email + email: email, } - stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json") - .with( - query: { - include_email: true, - include_entities: false, - skip_status: true - } - ) - .to_return(status: 200, body: JSON.dump(body)) + stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json").with( + query: { + include_email: true, + include_entities: false, + skip_status: true, + }, + ).to_return(status: 200, body: JSON.dump(body)) end before do @@ -66,28 +68,28 @@ describe 'Twitter OAuth 1.0a' do SiteSetting.twitter_consumer_key = consumer_key SiteSetting.twitter_consumer_secret = consumer_secret - stub_request(:post, "https://api.twitter.com/oauth/request_token") - .to_return( - status: 200, - body: Rack::Utils.build_query( + stub_request(:post, "https://api.twitter.com/oauth/request_token").to_return( + status: 200, + body: + Rack::Utils.build_query( oauth_token: access_token, oauth_token_secret: oauth_token_secret, - oauth_callback_confirmed: true + oauth_callback_confirmed: true, ), - headers: { - "Content-Type" => "application/x-www-form-urlencoded" - } - ) - stub_request(:post, "https://api.twitter.com/oauth/access_token") - .to_return( - status: 200, - body: Rack::Utils.build_query( + headers: { + "Content-Type" => "application/x-www-form-urlencoded", + }, + ) + stub_request(:post, "https://api.twitter.com/oauth/access_token").to_return( + status: 200, + body: + Rack::Utils.build_query( oauth_token: access_token, oauth_token_secret: oauth_token_secret, user_id: "43423432422", - screen_name: "twitterapi" - ) - ) + screen_name: "twitterapi", + ), + ) end it "signs in the user if the API response from twitter includes an email (implies it's verified) and the email matches an existing user's" do diff --git a/spec/integration/watched_words_spec.rb b/spec/integration/watched_words_spec.rb index 2eef6fdc324..2304fb6bce0 100644 --- a/spec/integration/watched_words_spec.rb +++ b/spec/integration/watched_words_spec.rb @@ -8,80 +8,121 @@ RSpec.describe WatchedWord do fab!(:topic) { Fabricate(:topic) } fab!(:first_post) { Fabricate(:post, topic: topic) } - let(:require_approval_word) { Fabricate(:watched_word, action: WatchedWord.actions[:require_approval]) } + let(:require_approval_word) do + Fabricate(:watched_word, action: WatchedWord.actions[:require_approval]) + end let(:flag_word) { Fabricate(:watched_word, action: WatchedWord.actions[:flag]) } let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) } let(:another_block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) } - before_all do - WordWatcher.clear_cache! - end + before_all { WordWatcher.clear_cache! } - after do - WordWatcher.clear_cache! - end + after { WordWatcher.clear_cache! } context "with block" do def should_block_post(manager) expect { result = manager.perform expect(result).to_not be_success - expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_word', word: block_word.word)) + expect(result.errors[:base]&.first).to eq( + I18n.t("contains_blocked_word", word: block_word.word), + ) }.to_not change { Post.count } end it "escapes the blocked word in error message" do block_word = Fabricate(:watched_word, action: WatchedWord.actions[:block], word: "") - manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id) + manager = + NewPostManager.new( + tl2_user, + raw: "Want some #{block_word.word} for cheap?", + topic_id: topic.id, + ) result = manager.perform expect(result).to_not be_success - expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_word', word: "<a>")) + expect(result.errors[:base]&.first).to eq(I18n.t("contains_blocked_word", word: "<a>")) end it "should prevent the post from being created" do - manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id) + manager = + NewPostManager.new( + tl2_user, + raw: "Want some #{block_word.word} for cheap?", + topic_id: topic.id, + ) should_block_post(manager) end it "look at title too" do - manager = NewPostManager.new(tl2_user, title: "We sell #{block_word.word} online", raw: "Want some poutine for cheap?", topic_id: topic.id) + manager = + NewPostManager.new( + tl2_user, + title: "We sell #{block_word.word} online", + raw: "Want some poutine for cheap?", + topic_id: topic.id, + ) should_block_post(manager) end it "should block the post from admin" do - manager = NewPostManager.new(admin, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id) + manager = + NewPostManager.new( + admin, + raw: "Want some #{block_word.word} for cheap?", + topic_id: topic.id, + ) should_block_post(manager) end it "should block the post from moderator" do - manager = NewPostManager.new(moderator, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id) + manager = + NewPostManager.new( + moderator, + raw: "Want some #{block_word.word} for cheap?", + topic_id: topic.id, + ) should_block_post(manager) end it "should block the post if it contains multiple blocked words" do - manager = NewPostManager.new(moderator, raw: "Want some #{block_word.word} #{another_block_word.word} for cheap?", topic_id: topic.id) + manager = + NewPostManager.new( + moderator, + raw: "Want some #{block_word.word} #{another_block_word.word} for cheap?", + topic_id: topic.id, + ) expect { result = manager.perform expect(result).to_not be_success - expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_words', words: [block_word.word, another_block_word.word].sort.join(', '))) + expect(result.errors[:base]&.first).to eq( + I18n.t( + "contains_blocked_words", + words: [block_word.word, another_block_word.word].sort.join(", "), + ), + ) }.to_not change { Post.count } end it "should block in a private message too" do - manager = NewPostManager.new( - tl2_user, - raw: "Want some #{block_word.word} for cheap?", - title: 'this is a new title', - archetype: Archetype.private_message, - target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username - ) + manager = + NewPostManager.new( + tl2_user, + raw: "Want some #{block_word.word} for cheap?", + title: "this is a new title", + archetype: Archetype.private_message, + target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username, + ) should_block_post(manager) end it "blocks on revisions" do post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user) expect { - PostRevisor.new(post).revise!(post.user, { raw: "Want some #{block_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds) + PostRevisor.new(post).revise!( + post.user, + { raw: "Want some #{block_word.word} for cheap?" }, + revised_at: post.updated_at + 10.seconds, + ) expect(post.errors).to be_present post.reload }.to_not change { post.raw } @@ -90,27 +131,48 @@ RSpec.describe WatchedWord do context "with require_approval" do it "should queue the post for approval" do - manager = NewPostManager.new(tl2_user, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id) + manager = + NewPostManager.new( + tl2_user, + raw: "My dog's name is #{require_approval_word.word}.", + topic_id: topic.id, + ) result = manager.perform expect(result.action).to eq(:enqueued) expect(result.reason).to eq(:watched_word) end it "looks at title too" do - manager = NewPostManager.new(tl2_user, title: "You won't believe these #{require_approval_word.word} dog names!", raw: "My dog's name is Porkins.", topic_id: topic.id) + manager = + NewPostManager.new( + tl2_user, + title: "You won't believe these #{require_approval_word.word} dog names!", + raw: "My dog's name is Porkins.", + topic_id: topic.id, + ) result = manager.perform expect(result.action).to eq(:enqueued) end it "should not queue posts from admin" do - manager = NewPostManager.new(admin, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id) + manager = + NewPostManager.new( + admin, + raw: "My dog's name is #{require_approval_word.word}.", + topic_id: topic.id, + ) result = manager.perform expect(result).to be_success expect(result.action).to eq(:create_post) end it "should not queue posts from moderator" do - manager = NewPostManager.new(moderator, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id) + manager = + NewPostManager.new( + moderator, + raw: "My dog's name is #{require_approval_word.word}.", + topic_id: topic.id, + ) result = manager.perform expect(result).to be_success expect(result.action).to eq(:create_post) @@ -118,13 +180,14 @@ RSpec.describe WatchedWord do it "doesn't need approval in a private message" do Group.refresh_automatic_groups! - manager = NewPostManager.new( - tl2_user, - raw: "Want some #{require_approval_word.word} for cheap?", - title: 'this is a new title', - archetype: Archetype.private_message, - target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username - ) + manager = + NewPostManager.new( + tl2_user, + raw: "Want some #{require_approval_word.word} for cheap?", + title: "this is a new title", + archetype: Archetype.private_message, + target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username, + ) result = manager.perform expect(result).to be_success expect(result.action).to eq(:create_post) @@ -134,74 +197,122 @@ RSpec.describe WatchedWord do context "with flag" do def should_flag_post(author, raw, topic) post = Fabricate(:post, raw: raw, topic: topic, user: author) - expect { - Jobs::ProcessPost.new.execute(post_id: post.id) - }.to change { PostAction.count }.by(1) - expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) + expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { PostAction.count }.by( + 1, + ) + expect( + PostAction.where( + post_id: post.id, + post_action_type_id: PostActionType.types[:inappropriate], + ).exists?, + ).to eq(true) end def should_not_flag_post(author, raw, topic) post = Fabricate(:post, raw: raw, topic: topic, user: author) - expect { - Jobs::ProcessPost.new.execute(post_id: post.id) - }.to_not change { PostAction.count } + expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to_not change { PostAction.count } end it "should flag the post as inappropriate" do topic = Fabricate(:topic, user: tl2_user) post = Fabricate(:post, raw: "I said.... #{flag_word.word}", topic: topic, user: tl2_user) Jobs::ProcessPost.new.execute(post_id: post.id) - expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) + expect( + PostAction.where( + post_id: post.id, + post_action_type_id: PostActionType.types[:inappropriate], + ).exists?, + ).to eq(true) reviewable = ReviewableFlaggedPost.where(target: post) expect(reviewable).to be_present - expect(ReviewableScore.where(reviewable: reviewable, reason: 'watched_word')).to be_present + expect(ReviewableScore.where(reviewable: reviewable, reason: "watched_word")).to be_present end it "should look at the title too" do - should_flag_post(tl2_user, "I thought the movie was not bad actually.", Fabricate(:topic, user: tl2_user, title: "Read my #{flag_word.word} review!")) + should_flag_post( + tl2_user, + "I thought the movie was not bad actually.", + Fabricate(:topic, user: tl2_user, title: "Read my #{flag_word.word} review!"), + ) end it "shouldn't flag posts by admin" do - should_not_flag_post(admin, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: admin)) + should_not_flag_post( + admin, + "I thought the #{flag_word.word} was bad.", + Fabricate(:topic, user: admin), + ) end it "shouldn't flag posts by moderator" do - should_not_flag_post(moderator, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: moderator)) + should_not_flag_post( + moderator, + "I thought the #{flag_word.word} was bad.", + Fabricate(:topic, user: moderator), + ) end it "is compatible with flag_sockpuppets" do SiteSetting.flag_sockpuppets = true - ip_address = '182.189.119.174' + ip_address = "182.189.119.174" user1 = Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago) user2 = Fabricate(:user, ip_address: ip_address) first = create_post(user: user1, created_at: 2.days.ago) - sockpuppet_post = create_post(user: user2, topic: first.topic, raw: "I thought the #{flag_word.word} was bad.") + sockpuppet_post = + create_post( + user: user2, + topic: first.topic, + raw: "I thought the #{flag_word.word} was bad.", + ) expect(PostAction.where(post_id: sockpuppet_post.id).count).to eq(1) end it "flags in private message too" do - post = Fabricate(:private_message_post, raw: "Want some #{flag_word.word} for cheap?", user: tl2_user) - expect { - Jobs::ProcessPost.new.execute(post_id: post.id) - }.to change { PostAction.count }.by(1) - expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) + post = + Fabricate( + :private_message_post, + raw: "Want some #{flag_word.word} for cheap?", + user: tl2_user, + ) + expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { PostAction.count }.by( + 1, + ) + expect( + PostAction.where( + post_id: post.id, + post_action_type_id: PostActionType.types[:inappropriate], + ).exists?, + ).to eq(true) end it "flags on revisions" do Jobs.run_immediately! post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user) expect { - PostRevisor.new(post).revise!(post.user, { raw: "Want some #{flag_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds) + PostRevisor.new(post).revise!( + post.user, + { raw: "Want some #{flag_word.word} for cheap?" }, + revised_at: post.updated_at + 10.seconds, + ) }.to change { PostAction.count }.by(1) - expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) + expect( + PostAction.where( + post_id: post.id, + post_action_type_id: PostActionType.types[:inappropriate], + ).exists?, + ).to eq(true) end it "should not flag on rebake" do - post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user, raw: "I have coupon codes. Message me.") + post = + Fabricate( + :post, + topic: Fabricate(:topic, user: tl2_user), + user: tl2_user, + raw: "I have coupon codes. Message me.", + ) Fabricate(:watched_word, action: WatchedWord.actions[:flag], word: "coupon") - expect { - post.rebake! - }.to_not change { PostAction.count } + expect { post.rebake! }.to_not change { PostAction.count } end end end diff --git a/spec/integrity/coding_style_spec.rb b/spec/integrity/coding_style_spec.rb index b51e6da61f2..ebd3ade729a 100644 --- a/spec/integrity/coding_style_spec.rb +++ b/spec/integrity/coding_style_spec.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -def list_files(base_dir, pattern = '*') +def list_files(base_dir, pattern = "*") Dir[File.join("#{base_dir}", pattern)] end def list_js_files(base_dir) - list_files(base_dir, '**/*.es6') + list_files(base_dir, "**/*.es6") end def grep_files(files, regex) @@ -17,10 +17,10 @@ def grep_file(file, regex) lines.count > 0 ? file : nil end -RSpec.describe 'Coding style' do - describe 'Javascript' do +RSpec.describe "Coding style" do + describe "Javascript" do it 'prevents this.get("foo") pattern' do - js_files = list_js_files('app/assets/javascripts') + js_files = list_js_files("app/assets/javascripts") offenses = grep_files(js_files, /this\.get\("\w+"\)/) expect(offenses).to be_empty, <<~TEXT @@ -33,7 +33,7 @@ RSpec.describe 'Coding style' do end end - describe 'Post Migrations' do + describe "Post Migrations" do def check_offenses(files, method_name, constant_name) method_name_regex = /#{Regexp.escape(method_name)}/ constant_name_regex = /#{Regexp.escape(constant_name)}/ @@ -57,8 +57,8 @@ RSpec.describe 'Coding style' do contains_method_name ? contains_constant_name : true end - it 'ensures dropped tables and columns are stored in constants' do - migration_files = list_files('db/post_migrate', '**/*.rb') + it "ensures dropped tables and columns are stored in constants" do + migration_files = list_files("db/post_migrate", "**/*.rb") check_offenses(migration_files, "ColumnDropper.execute_drop", "DROPPED_COLUMNS") check_offenses(migration_files, "TableDropper.execute_drop", "DROPPED_TABLES") diff --git a/spec/integrity/common_mark_spec.rb b/spec/integrity/common_mark_spec.rb index 01cc77683f6..c89f9ff0e81 100644 --- a/spec/integrity/common_mark_spec.rb +++ b/spec/integrity/common_mark_spec.rb @@ -1,76 +1,72 @@ # frozen_string_literal: true RSpec.describe "CommonMark" do - it 'passes spec' do - + it "passes spec" do SiteSetting.traditional_markdown_linebreaks = true SiteSetting.enable_markdown_typographer = false - SiteSetting.highlighted_languages = 'ruby|aa' + SiteSetting.highlighted_languages = "ruby|aa" html, state, md = nil failed = 0 - File.readlines(Rails.root + 'spec/fixtures/md/spec.txt').each do |line| - if line == "```````````````````````````````` example\n" - state = :example - next - end - - if line == "````````````````````````````````\n" - md.gsub!('→', "\t") - html ||= String.new - html.gsub!('→', "\t") - html.strip! - - # normalize brs - html.gsub!('
', '
') - html.gsub!('
', '
') - html.gsub!(/]+) \/>/, "") - - SiteSetting.enable_markdown_linkify = false - cooked = PrettyText.markdown(md, sanitize: false) - cooked.strip! - cooked.gsub!(" class=\"lang-auto\"", '') - cooked.gsub!(/(.*)<\/span>/, "\\1") - cooked.gsub!(/
<\/a>/, "") - # we support data-attributes which is not in the spec - cooked.gsub!("
", '
')
-        # we don't care about this
-        cooked.gsub!("
\n
", "
") - html.gsub!("
\n
", "
") - html.gsub!("language-ruby", "lang-ruby") - html.gsub!("language-aa", "lang-aa") - # strip out unsupported languages - html.gsub!(/ class="language-[;f].*"/, "") - - unless cooked == html - failed += 1 - puts "FAILED SPEC" - puts "Expected: " - puts html - puts "Got: " - puts cooked - puts "Markdown: " - puts md - puts + File + .readlines(Rails.root + "spec/fixtures/md/spec.txt") + .each do |line| + if line == "```````````````````````````````` example\n" + state = :example + next end - html, state, md = nil - next - end - if state == :example && line == ".\n" - state = :html - next - end + if line == "````````````````````````````````\n" + md.gsub!("→", "\t") + html ||= String.new + html.gsub!("→", "\t") + html.strip! - if state == :example - md = (md || String.new) << line - end + # normalize brs + html.gsub!("
", "
") + html.gsub!("
", "
") + html.gsub!(%r{]+) />}, "") - if state == :html - html = (html || String.new) << line - end + SiteSetting.enable_markdown_linkify = false + cooked = PrettyText.markdown(md, sanitize: false) + cooked.strip! + cooked.gsub!(" class=\"lang-auto\"", "") + cooked.gsub!(%r{(.*)}, "\\1") + cooked.gsub!(%r{
}, "") + # we support data-attributes which is not in the spec + cooked.gsub!("
", "
")
+          # we don't care about this
+          cooked.gsub!("
\n
", "
") + html.gsub!("
\n
", "
") + html.gsub!("language-ruby", "lang-ruby") + html.gsub!("language-aa", "lang-aa") + # strip out unsupported languages + html.gsub!(%r{ class="language-[;f].*"}, "") - end + unless cooked == html + failed += 1 + puts "FAILED SPEC" + puts "Expected: " + puts html + puts "Got: " + puts cooked + puts "Markdown: " + puts md + puts + end + html, state, md = nil + next + end + + if state == :example && line == ".\n" + state = :html + next + end + + md = (md || String.new) << line if state == :example + + html = (html || String.new) << line if state == :html + end expect(failed).to eq(0) end diff --git a/spec/integrity/i18n_spec.rb b/spec/integrity/i18n_spec.rb index a78811a395b..edf7bea714b 100644 --- a/spec/integrity/i18n_spec.rb +++ b/spec/integrity/i18n_spec.rb @@ -37,10 +37,12 @@ RSpec.describe "i18n integrity checks" do end it "has an i18n key for each Badge description" do - Badge.where(system: true).each do |b| - expect(b.long_description).to be_present - expect(b.description).to be_present - end + Badge + .where(system: true) + .each do |b| + expect(b.long_description).to be_present + expect(b.description).to be_present + end end Dir["#{Rails.root}/config/locales/{client,server}.*.yml"].each do |path| @@ -116,20 +118,14 @@ RSpec.describe "fallbacks" do it "finds the fallback translation" do I18n.backend.store_translations(:en, test: "en test") - I18n.with_locale("pl_PL") do - expect(I18n.t("test")).to eq("en test") - end + I18n.with_locale("pl_PL") { expect(I18n.t("test")).to eq("en test") } end context "when in a multi-threaded environment" do it "finds the fallback translation" do I18n.backend.store_translations(:en, test: "en test") - thread = Thread.new do - I18n.with_locale("pl_PL") do - expect(I18n.t("test")).to eq("en test") - end - end + thread = Thread.new { I18n.with_locale("pl_PL") { expect(I18n.t("test")).to eq("en test") } } begin thread.join diff --git a/spec/integrity/js_constants_spec.rb b/spec/integrity/js_constants_spec.rb index fe13669d549..559f2c45337 100644 --- a/spec/integrity/js_constants_spec.rb +++ b/spec/integrity/js_constants_spec.rb @@ -1,13 +1,12 @@ # frozen_string_literal: true RSpec.describe "constants match ruby" do - let(:ctx) { MiniRacer::Context.new } def parse(file) # mini racer doesn't handle JS modules so we'll do this hack source = File.read("#{Rails.root}/app/assets/javascripts/#{file}") - source.gsub!(/^export */, '') + source.gsub!(/^export */, "") ctx.eval(source) end @@ -16,12 +15,9 @@ RSpec.describe "constants match ruby" do parse("pretty-text/addon/emoji/version.js") priorities = ctx.eval("SEARCH_PRIORITIES") - Searchable::PRIORITIES.each do |key, value| - expect(priorities[key.to_s]).to eq(value) - end + Searchable::PRIORITIES.each { |key, value| expect(priorities[key.to_s]).to eq(value) } expect(ctx.eval("SEARCH_PHRASE_REGEXP")).to eq(Search::PHRASE_MATCH_REGEXP_PATTERN) expect(ctx.eval("IMAGE_VERSION")).to eq(Emoji::EMOJI_VERSION) end - end diff --git a/spec/integrity/oj_spec.rb b/spec/integrity/oj_spec.rb index 799f7578f2b..ede78a648ad 100644 --- a/spec/integrity/oj_spec.rb +++ b/spec/integrity/oj_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe 'Oj' do +RSpec.describe "Oj" do it "is enabled" do classes = Set.new tracer = TracePoint.new(:c_call) { |tp| classes << tp.defined_class } @@ -11,7 +11,7 @@ RSpec.describe 'Oj' do it "escapes HTML entities the same as ActiveSupport" do expect("hello".to_json).to eq("\"\\u003cb\\u003ehello\\u003c/b\\u003e\"") - expect('"hello world"'.to_json). to eq('"\"hello world\""') + expect('"hello world"'.to_json).to eq('"\"hello world\""') expect("\u2028\u2029><&".to_json).to eq('"\u2028\u2029\u003e\u003c\u0026"') end end diff --git a/spec/integrity/onceoff_integrity_spec.rb b/spec/integrity/onceoff_integrity_spec.rb index e913d2c8160..2952cfa1be8 100644 --- a/spec/integrity/onceoff_integrity_spec.rb +++ b/spec/integrity/onceoff_integrity_spec.rb @@ -3,11 +3,12 @@ RSpec.describe ::Jobs::Onceoff do it "can run all once off jobs without errors" do # Load all once offs - Dir[Rails.root + 'app/jobs/onceoff/*.rb'].each do |f| - require_relative '../../app/jobs/onceoff/' + File.basename(f) + Dir[Rails.root + "app/jobs/onceoff/*.rb"].each do |f| + require_relative "../../app/jobs/onceoff/" + File.basename(f) end - ObjectSpace.each_object(Class) + ObjectSpace + .each_object(Class) .select { |klass| klass.superclass == ::Jobs::Onceoff } .each { |job| job.new.execute_onceoff(nil) } end diff --git a/spec/integrity/site_setting_spec.rb b/spec/integrity/site_setting_spec.rb index 8779af45769..412902d8741 100644 --- a/spec/integrity/site_setting_spec.rb +++ b/spec/integrity/site_setting_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true RSpec.describe "site setting integrity checks" do - let(:site_setting_file) { File.join(Rails.root, 'config', 'site_settings.yml') } + let(:site_setting_file) { File.join(Rails.root, "config", "site_settings.yml") } let(:yaml) { YAML.load_file(site_setting_file) } - %w(hidden client).each do |property| + %w[hidden client].each do |property| it "set #{property} value as true or not set" do yaml.each_value do |category| category.each_value do |setting| @@ -24,16 +24,16 @@ RSpec.describe "site setting integrity checks" do yaml.each_value do |category| category.each do |setting_name, setting| next unless setting.is_a?(Hash) - if setting['locale_default'] - setting['locale_default'].each_pair do |k, v| + if setting["locale_default"] + setting["locale_default"].each_pair do |k, v| expect(LocaleSiteSetting.valid_value?(k.to_s)).to be_truthy, - "'#{k}' is not a valid locale_default key for '#{setting_name}' site setting" + "'#{k}' is not a valid locale_default key for '#{setting_name}' site setting" - case setting['default'] + case setting["default"] when TrueClass, FalseClass expect(v.class == TrueClass || v.class == FalseClass).to be_truthy else - expect(v).to be_a_kind_of(setting['default'].class) + expect(v).to be_a_kind_of(setting["default"].class) end end end diff --git a/spec/jobs/about_stats_spec.rb b/spec/jobs/about_stats_spec.rb index 3a2e5d6aa90..fa6ecaa0168 100644 --- a/spec/jobs/about_stats_spec.rb +++ b/spec/jobs/about_stats_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Jobs::AboutStats do - it 'caches the stats' do + it "caches the stats" do begin stats = About.fetch_stats.to_json cache_key = About.stats_cache_key diff --git a/spec/jobs/activation_reminder_emails_spec.rb b/spec/jobs/activation_reminder_emails_spec.rb index d7956d3dcdf..e70cc9abaa9 100644 --- a/spec/jobs/activation_reminder_emails_spec.rb +++ b/spec/jobs/activation_reminder_emails_spec.rb @@ -6,33 +6,33 @@ RSpec.describe Jobs::ActivationReminderEmails do # should be between 2 and 3 days let(:created_at) { 50.hours.ago } - it 'should email inactive users' do + it "should email inactive users" do user = Fabricate(:user, active: false, created_at: created_at) - expect { described_class.new.execute({}) } - .to change { ActionMailer::Base.deliveries.size }.by(1) - .and change { user.email_tokens.count }.by(1) + expect { described_class.new.execute({}) }.to change { ActionMailer::Base.deliveries.size }.by( + 1, + ).and change { user.email_tokens.count }.by(1) - expect(user.custom_fields['activation_reminder']).to eq("t") + expect(user.custom_fields["activation_reminder"]).to eq("t") expect { described_class.new.execute({}) }.not_to change { ActionMailer::Base.deliveries.size } user.activate - expect(user.reload.custom_fields['activation_reminder']).to eq(nil) + expect(user.reload.custom_fields["activation_reminder"]).to eq(nil) end - it 'should not email active users' do + it "should not email active users" do user = Fabricate(:user, active: true, created_at: created_at) - expect { described_class.new.execute({}) } - .to not_change { ActionMailer::Base.deliveries.size } - .and not_change { user.email_tokens.count } + expect { described_class.new.execute({}) }.to not_change { + ActionMailer::Base.deliveries.size + }.and not_change { user.email_tokens.count } end - it 'should not email staged users' do + it "should not email staged users" do user = Fabricate(:user, active: false, staged: true, created_at: created_at) - expect { described_class.new.execute({}) } - .to not_change { ActionMailer::Base.deliveries.size } - .and not_change { user.email_tokens.count } + expect { described_class.new.execute({}) }.to not_change { + ActionMailer::Base.deliveries.size + }.and not_change { user.email_tokens.count } end end diff --git a/spec/jobs/auto_expire_user_api_keys_spec.rb b/spec/jobs/auto_expire_user_api_keys_spec.rb index abed8ceba61..5c6ce1c8a6c 100644 --- a/spec/jobs/auto_expire_user_api_keys_spec.rb +++ b/spec/jobs/auto_expire_user_api_keys_spec.rb @@ -4,12 +4,10 @@ RSpec.describe Jobs::AutoExpireUserApiKeys do fab!(:key1) { Fabricate(:readonly_user_api_key) } fab!(:key2) { Fabricate(:readonly_user_api_key) } - context 'when user api key is unused in last 1 days' do - before do - SiteSetting.expire_user_api_keys_days = 1 - end + context "when user api key is unused in last 1 days" do + before { SiteSetting.expire_user_api_keys_days = 1 } - it 'should revoke the key' do + it "should revoke the key" do freeze_time key1.update!(last_used_at: 2.days.ago) diff --git a/spec/jobs/auto_queue_handler_spec.rb b/spec/jobs/auto_queue_handler_spec.rb index 6fb6c2a9003..f4d1ada3afc 100644 --- a/spec/jobs/auto_queue_handler_spec.rb +++ b/spec/jobs/auto_queue_handler_spec.rb @@ -9,15 +9,15 @@ RSpec.describe Jobs::AutoQueueHandler do Fabricate(:user), Fabricate(:post), PostActionType.types[:spam], - message: 'this is the initial message' + message: "this is the initial message", ).perform end fab!(:post_action) { spam_result.post_action } - fab!(:old) { + fab!(:old) do spam_result.reviewable.update_column(:created_at, 61.days.ago) spam_result.reviewable - } + end fab!(:not_old) { Fabricate(:reviewable_flagged_post, created_at: 59.days.ago) } diff --git a/spec/jobs/automatic_group_membership_spec.rb b/spec/jobs/automatic_group_membership_spec.rb index 76efc6cd8ab..ce130987c08 100644 --- a/spec/jobs/automatic_group_membership_spec.rb +++ b/spec/jobs/automatic_group_membership_spec.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true RSpec.describe Jobs::AutomaticGroupMembership do - it "raises an error when the group id is missing" do - expect { Jobs::AutomaticGroupMembership.new.execute({}) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::AutomaticGroupMembership.new.execute({}) }.to raise_error( + Discourse::InvalidParameters, + ) end it "updates the membership" do @@ -14,19 +15,28 @@ RSpec.describe Jobs::AutomaticGroupMembership do user4 = Fabricate(:user, email: "yes@wat.com") EmailToken.confirm(Fabricate(:email_token, user: user4).token) user5 = Fabricate(:user, email: "sso@wat.com") - user5.create_single_sign_on_record(external_id: 123, external_email: "hacker@wat.com", last_payload: "") + user5.create_single_sign_on_record( + external_id: 123, + external_email: "hacker@wat.com", + last_payload: "", + ) user6 = Fabricate(:user, email: "sso2@wat.com") - user6.create_single_sign_on_record(external_id: 456, external_email: "sso2@wat.com", last_payload: "") + user6.create_single_sign_on_record( + external_id: 456, + external_email: "sso2@wat.com", + last_payload: "", + ) group = Fabricate(:group, automatic_membership_email_domains: "wat.com") automatic = nil called = false - blk = Proc.new do |_u, _g, options| - automatic = options[:automatic] - called = true - end + blk = + Proc.new do |_u, _g, options| + automatic = options[:automatic] + called = true + end begin DiscourseEvent.on(:user_added_to_group, &blk) @@ -47,5 +57,4 @@ RSpec.describe Jobs::AutomaticGroupMembership do expect(group.users.include?(user6)).to eq(true) expect(group.user_count).to eq(2) end - end diff --git a/spec/jobs/bookmark_reminder_notifications_spec.rb b/spec/jobs/bookmark_reminder_notifications_spec.rb index 6a68eb439e7..22a9a17f83b 100644 --- a/spec/jobs/bookmark_reminder_notifications_spec.rb +++ b/spec/jobs/bookmark_reminder_notifications_spec.rb @@ -8,13 +8,7 @@ RSpec.describe Jobs::BookmarkReminderNotifications do let(:bookmark1) { Fabricate(:bookmark, user: user) } let(:bookmark2) { Fabricate(:bookmark, user: user) } let(:bookmark3) { Fabricate(:bookmark, user: user) } - let!(:bookmarks) do - [ - bookmark1, - bookmark2, - bookmark3 - ] - end + let!(:bookmarks) { [bookmark1, bookmark2, bookmark3] } before do # this is done to avoid model validations on Bookmark @@ -66,10 +60,12 @@ RSpec.describe Jobs::BookmarkReminderNotifications do end end - it 'will not send notification when topic is not available' do + it "will not send notification when topic is not available" do bookmark1.bookmarkable.topic.destroy bookmark2.bookmarkable.topic.destroy bookmark3.bookmarkable.topic.destroy - expect { subject.execute }.not_to change { Notification.where(notification_type: Notification.types[:bookmark_reminder]).count } + expect { subject.execute }.not_to change { + Notification.where(notification_type: Notification.types[:bookmark_reminder]).count + } end end diff --git a/spec/jobs/bulk_grant_trust_level_spec.rb b/spec/jobs/bulk_grant_trust_level_spec.rb index bfda953eba1..b2963069da6 100644 --- a/spec/jobs/bulk_grant_trust_level_spec.rb +++ b/spec/jobs/bulk_grant_trust_level_spec.rb @@ -1,13 +1,16 @@ # frozen_string_literal: true RSpec.describe Jobs::BulkGrantTrustLevel do - it "raises an error when trust_level is missing" do - expect { Jobs::BulkGrantTrustLevel.new.execute(user_ids: [1, 2]) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::BulkGrantTrustLevel.new.execute(user_ids: [1, 2]) }.to raise_error( + Discourse::InvalidParameters, + ) end it "raises an error when user_ids are missing" do - expect { Jobs::BulkGrantTrustLevel.new.execute(trust_level: 0) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::BulkGrantTrustLevel.new.execute(trust_level: 0) }.to raise_error( + Discourse::InvalidParameters, + ) end it "updates the trust_level" do diff --git a/spec/jobs/bulk_invite_spec.rb b/spec/jobs/bulk_invite_spec.rb index 53d38d78885..3a8d370ab7e 100644 --- a/spec/jobs/bulk_invite_spec.rb +++ b/spec/jobs/bulk_invite_spec.rb @@ -1,34 +1,43 @@ # frozen_string_literal: true RSpec.describe Jobs::BulkInvite do - describe '#execute' do + describe "#execute" do fab!(:user) { Fabricate(:user) } fab!(:admin) { Fabricate(:admin) } - fab!(:group1) { Fabricate(:group, name: 'group1') } - fab!(:group2) { Fabricate(:group, name: 'group2') } + fab!(:group1) { Fabricate(:group, name: "group1") } + fab!(:group2) { Fabricate(:group, name: "group2") } fab!(:topic) { Fabricate(:topic) } let(:staged_user) { Fabricate(:user, staged: true, active: false) } - let(:email) { 'test@discourse.org' } - let(:invites) { [{ email: user.email }, { email: staged_user.email }, { email: 'test2@discourse.org' }, { email: 'test@discourse.org', groups: 'GROUP1;group2', topic_id: topic.id }, { email: 'invalid' }] } - - it 'raises an error when the invites array is missing' do - expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) } - .to raise_error(Discourse::InvalidParameters, /invites/) + let(:email) { "test@discourse.org" } + let(:invites) do + [ + { email: user.email }, + { email: staged_user.email }, + { email: "test2@discourse.org" }, + { email: "test@discourse.org", groups: "GROUP1;group2", topic_id: topic.id }, + { email: "invalid" }, + ] end - it 'raises an error when current_user_id is not valid' do - expect { Jobs::BulkInvite.new.execute(invites: invites) } - .to raise_error(Discourse::InvalidParameters, /current_user_id/) - end - - it 'creates the right invites' do - described_class.new.execute( - current_user_id: admin.id, - invites: invites + it "raises an error when the invites array is missing" do + expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) }.to raise_error( + Discourse::InvalidParameters, + /invites/, ) + end + + it "raises an error when current_user_id is not valid" do + expect { Jobs::BulkInvite.new.execute(invites: invites) }.to raise_error( + Discourse::InvalidParameters, + /current_user_id/, + ) + end + + it "creates the right invites" do + described_class.new.execute(current_user_id: admin.id, invites: invites) expect(Invite.exists?(email: staged_user.email)).to eq(true) - expect(Invite.exists?(email: 'test2@discourse.org')).to eq(true) + expect(Invite.exists?(email: "test2@discourse.org")).to eq(true) invite = Invite.last expect(invite.email).to eq(email) @@ -42,13 +51,10 @@ RSpec.describe Jobs::BulkInvite do expect(post.raw).to include("1 error") end - it 'does not create invited groups for automatic groups' do + it "does not create invited groups for automatic groups" do group2.update!(automatic: true) - described_class.new.execute( - current_user_id: admin.id, - invites: invites - ) + described_class.new.execute(current_user_id: admin.id, invites: invites) invite = Invite.last expect(invite.email).to eq(email) @@ -58,37 +64,31 @@ RSpec.describe Jobs::BulkInvite do expect(post.raw).to include("1 warning") end - it 'does not create invited groups record if the user can not manage the group' do + it "does not create invited groups record if the user can not manage the group" do group1.add_owner(user) - described_class.new.execute( - current_user_id: user.id, - invites: invites - ) + described_class.new.execute(current_user_id: user.id, invites: invites) invite = Invite.last expect(invite.email).to eq(email) expect(invite.invited_groups.pluck(:group_id)).to contain_exactly(group1.id) end - it 'adds existing users to valid groups' do - existing_user = Fabricate(:user, email: 'test@discourse.org') + it "adds existing users to valid groups" do + existing_user = Fabricate(:user, email: "test@discourse.org") group2.update!(automatic: true) expect do - described_class.new.execute( - current_user_id: admin.id, - invites: invites - ) + described_class.new.execute(current_user_id: admin.id, invites: invites) end.to change { Invite.count }.by(2) expect(Invite.exists?(email: staged_user.email)).to eq(true) - expect(Invite.exists?(email: 'test2@discourse.org')).to eq(true) + expect(Invite.exists?(email: "test2@discourse.org")).to eq(true) expect(existing_user.reload.groups).to eq([group1]) end - it 'can create staged users and prepopulate user fields' do + it "can create staged users and prepopulate user fields" do user_field = Fabricate(:user_field, name: "Location") user_field_color = Fabricate(:user_field, field_type: "dropdown", name: "Color") user_field_color.user_field_options.create!(value: "Red") @@ -98,40 +98,33 @@ RSpec.describe Jobs::BulkInvite do described_class.new.execute( current_user_id: admin.id, invites: [ - { email: 'test@discourse.org' }, # new user without user fields - { email: user.email, location: 'value 1', color: 'blue' }, # existing user with user fields - { email: staged_user.email, location: 'value 2', color: 'redd' }, # existing staged user with user fields - { email: 'test2@discourse.org', location: 'value 3' } # new staged user with user fields - ] + { email: "test@discourse.org" }, # new user without user fields + { email: user.email, location: "value 1", color: "blue" }, # existing user with user fields + { email: staged_user.email, location: "value 2", color: "redd" }, # existing staged user with user fields + { email: "test2@discourse.org", location: "value 3" }, # new staged user with user fields + ], ) expect(Invite.count).to eq(3) - expect(User.where(staged: true).find_by_email('test@discourse.org')).to eq(nil) - expect(user.user_fields[user_field.id.to_s]).to eq('value 1') - expect(user.user_fields[user_field_color.id.to_s]).to eq('Blue') - expect(staged_user.user_fields[user_field.id.to_s]).to eq('value 2') + expect(User.where(staged: true).find_by_email("test@discourse.org")).to eq(nil) + expect(user.user_fields[user_field.id.to_s]).to eq("value 1") + expect(user.user_fields[user_field_color.id.to_s]).to eq("Blue") + expect(staged_user.user_fields[user_field.id.to_s]).to eq("value 2") expect(staged_user.user_fields[user_field_color.id.to_s]).to eq(nil) - new_staged_user = User.where(staged: true).find_by_email('test2@discourse.org') - expect(new_staged_user.user_fields[user_field.id.to_s]).to eq('value 3') + new_staged_user = User.where(staged: true).find_by_email("test2@discourse.org") + expect(new_staged_user.user_fields[user_field.id.to_s]).to eq("value 3") end - context 'when there are more than 200 invites' do + context "when there are more than 200 invites" do let(:bulk_invites) { [] } - before do - 202.times do |i| - bulk_invites << { "email": "test_#{i}@discourse.org" } - end - end + before { 202.times { |i| bulk_invites << { email: "test_#{i}@discourse.org" } } } - it 'rate limits email sending' do - described_class.new.execute( - current_user_id: admin.id, - invites: bulk_invites - ) + it "rate limits email sending" do + described_class.new.execute(current_user_id: admin.id, invites: bulk_invites) invite = Invite.last - expect(invite.email).to eq('test_201@discourse.org') + expect(invite.email).to eq("test_201@discourse.org") expect(invite.emailed_status).to eq(Invite.emailed_status_types[:bulk_pending]) expect(Jobs::ProcessBulkInviteEmails.jobs.size).to eq(1) end diff --git a/spec/jobs/bump_topic_spec.rb b/spec/jobs/bump_topic_spec.rb index ed7668d9c53..ce4d2aecbee 100644 --- a/spec/jobs/bump_topic_spec.rb +++ b/spec/jobs/bump_topic_spec.rb @@ -31,5 +31,4 @@ RSpec.describe Jobs::BumpTopic do expect(topic.reload.public_topic_timer).to eq(nil) end - end diff --git a/spec/jobs/check_new_features_spec.rb b/spec/jobs/check_new_features_spec.rb index ab20272145b..43fb9be37fa 100644 --- a/spec/jobs/check_new_features_spec.rb +++ b/spec/jobs/check_new_features_spec.rb @@ -4,53 +4,40 @@ RSpec.describe Jobs::CheckNewFeatures do def build_feature_hash(id:, created_at:, discourse_version: "2.9.0.beta10") { id: id, - user_id: 89432, + user_id: 89_432, emoji: "👤", title: "New fancy feature!", description: "", link: "https://meta.discourse.org/t/-/238821", created_at: created_at.iso8601, updated_at: (created_at + 1.minutes).iso8601, - discourse_version: discourse_version + discourse_version: discourse_version, } end def stub_meta_new_features_endpoint(*features) - stub_request(:get, "https://meta.discourse.org/new-features.json") - .to_return( - status: 200, - body: JSON.dump(features), - headers: { - "Content-Type" => "application/json" - } - ) + stub_request(:get, "https://meta.discourse.org/new-features.json").to_return( + status: 200, + body: JSON.dump(features), + headers: { + "Content-Type" => "application/json", + }, + ) end fab!(:admin1) { Fabricate(:admin) } fab!(:admin2) { Fabricate(:admin) } let(:feature1) do - build_feature_hash( - id: 35, - created_at: 3.days.ago, - discourse_version: "2.8.1.beta12" - ) + build_feature_hash(id: 35, created_at: 3.days.ago, discourse_version: "2.8.1.beta12") end let(:feature2) do - build_feature_hash( - id: 34, - created_at: 2.days.ago, - discourse_version: "2.8.1.beta13" - ) + build_feature_hash(id: 34, created_at: 2.days.ago, discourse_version: "2.8.1.beta13") end let(:pending_feature) do - build_feature_hash( - id: 37, - created_at: 1.day.ago, - discourse_version: "2.8.1.beta14" - ) + build_feature_hash(id: 37, created_at: 1.day.ago, discourse_version: "2.8.1.beta14") end before do @@ -59,9 +46,7 @@ RSpec.describe Jobs::CheckNewFeatures do stub_meta_new_features_endpoint(feature1, feature2, pending_feature) end - after do - DiscourseUpdates.clean_state - end + after { DiscourseUpdates.clean_state } it "backfills last viewed feature for admins who don't have last viewed feature" do DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta12") @@ -70,8 +55,12 @@ RSpec.describe Jobs::CheckNewFeatures do described_class.new.execute({}) - expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id).iso8601).to eq(feature1[:created_at]) - expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id).iso8601).to eq(Time.zone.now.iso8601) + expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id).iso8601).to eq( + feature1[:created_at], + ) + expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id).iso8601).to eq( + Time.zone.now.iso8601, + ) end it "notifies admins about new features that are available in the site's version" do @@ -79,14 +68,18 @@ RSpec.describe Jobs::CheckNewFeatures do described_class.new.execute({}) - expect(admin1.notifications.where( - notification_type: Notification.types[:new_features], - read: false - ).count).to eq(1) - expect(admin2.notifications.where( - notification_type: Notification.types[:new_features], - read: false - ).count).to eq(1) + expect( + admin1 + .notifications + .where(notification_type: Notification.types[:new_features], read: false) + .count, + ).to eq(1) + expect( + admin2 + .notifications + .where(notification_type: Notification.types[:new_features], read: false) + .count, + ).to eq(1) end it "consolidates new features notifications" do @@ -94,10 +87,11 @@ RSpec.describe Jobs::CheckNewFeatures do described_class.new.execute({}) - notification = admin1.notifications.where( - notification_type: Notification.types[:new_features], - read: false - ).first + notification = + admin1 + .notifications + .where(notification_type: Notification.types[:new_features], read: false) + .first expect(notification).to be_present DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta14") @@ -106,10 +100,11 @@ RSpec.describe Jobs::CheckNewFeatures do # old notification is destroyed expect(Notification.find_by(id: notification.id)).to eq(nil) - notification = admin1.notifications.where( - notification_type: Notification.types[:new_features], - read: false - ).first + notification = + admin1 + .notifications + .where(notification_type: Notification.types[:new_features], read: false) + .first # new notification is created expect(notification).to be_present end @@ -121,6 +116,8 @@ RSpec.describe Jobs::CheckNewFeatures do described_class.new.execute({}) expect(admin1.notifications.count).to eq(0) - expect(admin2.notifications.where(notification_type: Notification.types[:new_features]).count).to eq(1) + expect( + admin2.notifications.where(notification_type: Notification.types[:new_features]).count, + ).to eq(1) end end diff --git a/spec/jobs/clean_dismissed_topic_users_spec.rb b/spec/jobs/clean_dismissed_topic_users_spec.rb index 187ce7c79d0..b6e411814bb 100644 --- a/spec/jobs/clean_dismissed_topic_users_spec.rb +++ b/spec/jobs/clean_dismissed_topic_users_spec.rb @@ -5,13 +5,13 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do fab!(:topic) { Fabricate(:topic, created_at: 5.hours.ago) } fab!(:dismissed_topic_user) { Fabricate(:dismissed_topic_user, user: user, topic: topic) } - describe '#delete_overdue_dismissals!' do - it 'does not delete when new_topic_duration_minutes is set to always' do + describe "#delete_overdue_dismissals!" do + it "does not delete when new_topic_duration_minutes is set to always" do user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS) expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count } end - it 'deletes when new_topic_duration_minutes is set to since last visit' do + it "deletes when new_topic_duration_minutes is set to since last visit" do user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::LAST_VISIT) expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count } @@ -19,7 +19,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do expect { described_class.new.execute({}) }.to change { DismissedTopicUser.count }.by(-1) end - it 'deletes when new_topic_duration_minutes is set to created in the last day' do + it "deletes when new_topic_duration_minutes is set to created in the last day" do user.user_option.update(new_topic_duration_minutes: 1440) expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count } @@ -28,7 +28,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do end end - describe '#delete_over_the_limit_dismissals!' do + describe "#delete_over_the_limit_dismissals!" do fab!(:user2) { Fabricate(:user, created_at: 1.days.ago, previous_visit_at: 1.days.ago) } fab!(:topic2) { Fabricate(:topic, created_at: 6.hours.ago) } fab!(:topic3) { Fabricate(:topic, created_at: 2.hours.ago) } @@ -41,7 +41,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do user2.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS) end - it 'deletes over the limit dismissals' do + it "deletes over the limit dismissals" do described_class.new.execute({}) expect(dismissed_topic_user.reload).to be_present expect(dismissed_topic_user2.reload).to be_present diff --git a/spec/jobs/clean_up_associated_accounts_spec.rb b/spec/jobs/clean_up_associated_accounts_spec.rb index 433c5fcaaca..cefcac82bde 100644 --- a/spec/jobs/clean_up_associated_accounts_spec.rb +++ b/spec/jobs/clean_up_associated_accounts_spec.rb @@ -6,12 +6,27 @@ RSpec.describe Jobs::CleanUpAssociatedAccounts do it "deletes the correct records" do freeze_time - last_week = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "1", updated_at: 7.days.ago) - today = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "12", updated_at: 12.hours.ago) - connected = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "123", user: Fabricate(:user), updated_at: 12.hours.ago) + last_week = + UserAssociatedAccount.create!( + provider_name: "twitter", + provider_uid: "1", + updated_at: 7.days.ago, + ) + today = + UserAssociatedAccount.create!( + provider_name: "twitter", + provider_uid: "12", + updated_at: 12.hours.ago, + ) + connected = + UserAssociatedAccount.create!( + provider_name: "twitter", + provider_uid: "123", + user: Fabricate(:user), + updated_at: 12.hours.ago, + ) expect { subject }.to change { UserAssociatedAccount.count }.by(-1) expect(UserAssociatedAccount.all).to contain_exactly(today, connected) end - end diff --git a/spec/jobs/clean_up_email_logs_spec.rb b/spec/jobs/clean_up_email_logs_spec.rb index 3d0ec29b09a..b4e7669675a 100644 --- a/spec/jobs/clean_up_email_logs_spec.rb +++ b/spec/jobs/clean_up_email_logs_spec.rb @@ -5,9 +5,7 @@ RSpec.describe Jobs::CleanUpEmailLogs do fab!(:email_log2) { Fabricate(:email_log, created_at: 2.weeks.ago) } fab!(:email_log3) { Fabricate(:email_log, created_at: 2.days.ago) } - let!(:skipped_email_log) do - Fabricate(:skipped_email_log, created_at: 2.years.ago) - end + let!(:skipped_email_log) { Fabricate(:skipped_email_log, created_at: 2.years.ago) } fab!(:skipped_email_log2) { Fabricate(:skipped_email_log) } @@ -23,10 +21,6 @@ RSpec.describe Jobs::CleanUpEmailLogs do expect(EmailLog.all).to contain_exactly(email_log, email_log2, email_log3) - expect(SkippedEmailLog.all).to contain_exactly( - skipped_email_log, - skipped_email_log2 - ) + expect(SkippedEmailLog.all).to contain_exactly(skipped_email_log, skipped_email_log2) end - end diff --git a/spec/jobs/clean_up_inactive_users_spec.rb b/spec/jobs/clean_up_inactive_users_spec.rb index 55bc28625ff..985659519d7 100644 --- a/spec/jobs/clean_up_inactive_users_spec.rb +++ b/spec/jobs/clean_up_inactive_users_spec.rb @@ -4,22 +4,16 @@ RSpec.describe Jobs::CleanUpInactiveUsers do it "should clean up new users that have been inactive" do SiteSetting.clean_up_inactive_users_after_days = 0 - user = Fabricate(:user, - last_seen_at: 5.days.ago, - trust_level: TrustLevel.levels[:newuser] - ) + user = Fabricate(:user, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser]) Fabricate(:active_user) - Fabricate(:post, user: Fabricate(:user, - trust_level: TrustLevel.levels[:newuser], - last_seen_at: 5.days.ago - )).user + Fabricate( + :post, + user: Fabricate(:user, trust_level: TrustLevel.levels[:newuser], last_seen_at: 5.days.ago), + ).user - Fabricate(:user, - trust_level: TrustLevel.levels[:newuser], - last_seen_at: 2.days.ago - ) + Fabricate(:user, trust_level: TrustLevel.levels[:newuser], last_seen_at: 2.days.ago) Fabricate(:user, trust_level: TrustLevel.levels[:basic]) @@ -27,8 +21,7 @@ RSpec.describe Jobs::CleanUpInactiveUsers do SiteSetting.clean_up_inactive_users_after_days = 4 - expect { described_class.new.execute({}) } - .to change { User.count }.by(-1) + expect { described_class.new.execute({}) }.to change { User.count }.by(-1) expect(User.exists?(id: user.id)).to eq(false) end @@ -43,7 +36,8 @@ RSpec.describe Jobs::CleanUpInactiveUsers do it "doesn't delete inactive mods" do SiteSetting.clean_up_inactive_users_after_days = 4 - moderator = Fabricate(:moderator, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser]) + moderator = + Fabricate(:moderator, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser]) expect { described_class.new.execute({}) }.to_not change { User.count } expect(User.exists?(moderator.id)).to eq(true) diff --git a/spec/jobs/clean_up_post_reply_keys_spec.rb b/spec/jobs/clean_up_post_reply_keys_spec.rb index 10594c1ac5e..bd20b4f1dfc 100644 --- a/spec/jobs/clean_up_post_reply_keys_spec.rb +++ b/spec/jobs/clean_up_post_reply_keys_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Jobs::CleanUpPostReplyKeys do - it 'removes old post_reply_keys' do + it "removes old post_reply_keys" do freeze_time reply_key1 = Fabricate(:post_reply_key, created_at: 1.day.ago) @@ -10,16 +10,12 @@ RSpec.describe Jobs::CleanUpPostReplyKeys do SiteSetting.disallow_reply_by_email_after_days = 0 - expect { Jobs::CleanUpPostReplyKeys.new.execute({}) } - .not_to change { PostReplyKey.count } + expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }.not_to change { PostReplyKey.count } SiteSetting.disallow_reply_by_email_after_days = 2 - expect { Jobs::CleanUpPostReplyKeys.new.execute({}) } - .to change { PostReplyKey.count }.by(-1) + expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }.to change { PostReplyKey.count }.by(-1) - expect(PostReplyKey.all).to contain_exactly( - reply_key1, reply_key2 - ) + expect(PostReplyKey.all).to contain_exactly(reply_key1, reply_key2) end end diff --git a/spec/jobs/clean_up_unused_staged_users_spec.rb b/spec/jobs/clean_up_unused_staged_users_spec.rb index daaad64912f..f66bc604603 100644 --- a/spec/jobs/clean_up_unused_staged_users_spec.rb +++ b/spec/jobs/clean_up_unused_staged_users_spec.rb @@ -37,7 +37,7 @@ RSpec.describe Jobs::CleanUpUnusedStagedUsers do end end - context 'when staged user is not old enough' do + context "when staged user is not old enough" do before { staged_user.update!(created_at: 5.months.ago) } include_examples "does not delete" end diff --git a/spec/jobs/clean_up_uploads_spec.rb b/spec/jobs/clean_up_uploads_spec.rb index 461123394b4..39ba932329a 100644 --- a/spec/jobs/clean_up_uploads_spec.rb +++ b/spec/jobs/clean_up_uploads_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true RSpec.describe Jobs::CleanUpUploads do - def fabricate_upload(attributes = {}) Fabricate(:upload, { created_at: 2.hours.ago }.merge(attributes)) end @@ -22,7 +21,6 @@ RSpec.describe Jobs::CleanUpUploads do end it "only runs upload cleanup every grace period / 2 time" do - SiteSetting.clean_orphan_uploads_grace_period_hours = 48 expired = fabricate_upload(created_at: 49.hours.ago) Jobs::CleanUpUploads.new.execute(nil) @@ -38,44 +36,31 @@ RSpec.describe Jobs::CleanUpUploads do Jobs::CleanUpUploads.new.execute(nil) expect(Upload.exists?(id: upload.id)).to eq(false) - end it "deletes orphan uploads" do - expect do - Jobs::CleanUpUploads.new.execute(nil) - end.to change { Upload.count }.by(-1) + expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1) expect(Upload.exists?(id: expired_upload.id)).to eq(false) end - describe 'unused callbacks' do - before do - Upload.add_unused_callback do |uploads| - uploads.where.not(id: expired_upload.id) - end - end + describe "unused callbacks" do + before { Upload.add_unused_callback { |uploads| uploads.where.not(id: expired_upload.id) } } - after do - Upload.reset_unused_callbacks - end + after { Upload.reset_unused_callbacks } - it 'does not delete uploads skipped by an unused callback' do - expect do - Jobs::CleanUpUploads.new.execute(nil) - end.not_to change { Upload.count } + it "does not delete uploads skipped by an unused callback" do + expect do Jobs::CleanUpUploads.new.execute(nil) end.not_to change { Upload.count } expect(Upload.exists?(id: expired_upload.id)).to eq(true) end - it 'deletes other uploads not skipped by an unused callback' do + it "deletes other uploads not skipped by an unused callback" do expired_upload2 = fabricate_upload upload = fabricate_upload UploadReference.create(target: Fabricate(:post), upload: upload) - expect do - Jobs::CleanUpUploads.new.execute(nil) - end.to change { Upload.count }.by(-1) + expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1) expect(Upload.exists?(id: expired_upload.id)).to eq(true) expect(Upload.exists?(id: expired_upload2.id)).to eq(false) @@ -83,33 +68,23 @@ RSpec.describe Jobs::CleanUpUploads do end end - describe 'in use callbacks' do - before do - Upload.add_in_use_callback do |upload| - expired_upload.id == upload.id - end - end + describe "in use callbacks" do + before { Upload.add_in_use_callback { |upload| expired_upload.id == upload.id } } - after do - Upload.reset_in_use_callbacks - end + after { Upload.reset_in_use_callbacks } - it 'does not delete uploads that are in use by callback' do - expect do - Jobs::CleanUpUploads.new.execute(nil) - end.not_to change { Upload.count } + it "does not delete uploads that are in use by callback" do + expect do Jobs::CleanUpUploads.new.execute(nil) end.not_to change { Upload.count } expect(Upload.exists?(id: expired_upload.id)).to eq(true) end - it 'deletes other uploads that are not in use by callback' do + it "deletes other uploads that are not in use by callback" do expired_upload2 = fabricate_upload upload = fabricate_upload UploadReference.create(target: Fabricate(:post), upload: upload) - expect do - Jobs::CleanUpUploads.new.execute(nil) - end.to change { Upload.count }.by(-1) + expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1) expect(Upload.exists?(id: expired_upload.id)).to eq(true) expect(Upload.exists?(id: expired_upload2.id)).to eq(false) @@ -117,27 +92,20 @@ RSpec.describe Jobs::CleanUpUploads do end end - describe 'when clean_up_uploads is disabled' do - before do - SiteSetting.clean_up_uploads = false - end + describe "when clean_up_uploads is disabled" do + before { SiteSetting.clean_up_uploads = false } - it 'should still delete invalid upload records' do - upload2 = fabricate_upload( - url: "", - retain_hours: nil - ) + it "should still delete invalid upload records" do + upload2 = fabricate_upload(url: "", retain_hours: nil) - expect do - Jobs::CleanUpUploads.new.execute(nil) - end.to change { Upload.count }.by(-1) + expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1) expect(Upload.exists?(id: expired_upload.id)).to eq(true) expect(Upload.exists?(id: upload2.id)).to eq(false) end end - it 'does not clean up upload site settings' do + it "does not clean up upload site settings" do begin original_provider = SiteSetting.provider SiteSetting.provider = SiteSettings::DbProvider.new(SiteSetting) @@ -161,8 +129,7 @@ RSpec.describe Jobs::CleanUpUploads do SiteSetting.large_icon = large_icon_upload SiteSetting.opengraph_image = opengraph_image_upload - SiteSetting.twitter_summary_large_image = - twitter_summary_large_image_upload + SiteSetting.twitter_summary_large_image = twitter_summary_large_image_upload SiteSetting.favicon = favicon_upload SiteSetting.apple_touch_icon = apple_touch_icon_upload @@ -170,20 +137,13 @@ RSpec.describe Jobs::CleanUpUploads do Jobs::CleanUpUploads.new.execute(nil) [ - logo_upload, - logo_small_upload, - digest_logo_upload, - mobile_logo_upload, - large_icon_upload, - opengraph_image_upload, - twitter_summary_large_image_upload, - favicon_upload, - apple_touch_icon_upload, - system_upload + logo_upload, logo_small_upload, digest_logo_upload, mobile_logo_upload, large_icon_upload, + opengraph_image_upload, twitter_summary_large_image_upload, favicon_upload, + apple_touch_icon_upload, system_upload, ].each { |record| expect(Upload.exists?(id: record.id)).to eq(true) } fabricate_upload - SiteSetting.opengraph_image = '' + SiteSetting.opengraph_image = "" Jobs::CleanUpUploads.new.execute(nil) ensure @@ -307,15 +267,19 @@ RSpec.describe Jobs::CleanUpUploads do upload2 = fabricate_upload upload3 = fabricate_upload - Fabricate(:reviewable_queued_post_topic, payload: { - raw: "#{upload.short_url}\n#{upload2.short_url}" - }) - - Fabricate(:reviewable_queued_post_topic, + Fabricate( + :reviewable_queued_post_topic, payload: { - raw: "#{upload3.short_url}" + raw: "#{upload.short_url}\n#{upload2.short_url}", }, - status: Reviewable.statuses[:rejected] + ) + + Fabricate( + :reviewable_queued_post_topic, + payload: { + raw: "#{upload3.short_url}", + }, + status: Reviewable.statuses[:rejected], ) Jobs::CleanUpUploads.new.execute(nil) @@ -350,7 +314,7 @@ RSpec.describe Jobs::CleanUpUploads do it "does not delete custom emojis" do upload = fabricate_upload - CustomEmoji.create!(name: 'test', upload: upload) + CustomEmoji.create!(name: "test", upload: upload) Jobs::CleanUpUploads.new.execute(nil) @@ -371,7 +335,12 @@ RSpec.describe Jobs::CleanUpUploads do it "does not delete theme setting uploads" do theme = Fabricate(:theme) theme_upload = fabricate_upload - ThemeSetting.create!(theme: theme, data_type: ThemeSetting.types[:upload], value: theme_upload.id.to_s, name: "my_setting_name") + ThemeSetting.create!( + theme: theme, + data_type: ThemeSetting.types[:upload], + value: theme_upload.id.to_s, + name: "my_setting_name", + ) Jobs::CleanUpUploads.new.execute(nil) @@ -390,10 +359,30 @@ RSpec.describe Jobs::CleanUpUploads do end it "deletes external upload stubs that have expired" do - external_stub1 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:created], created_at: 10.minutes.ago) - external_stub2 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:created], created_at: (ExternalUploadStub::CREATED_EXPIRY_HOURS.hours + 10.minutes).ago) - external_stub3 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:uploaded], created_at: 10.minutes.ago) - external_stub4 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:uploaded], created_at: (ExternalUploadStub::UPLOADED_EXPIRY_HOURS.hours + 10.minutes).ago) + external_stub1 = + Fabricate( + :external_upload_stub, + status: ExternalUploadStub.statuses[:created], + created_at: 10.minutes.ago, + ) + external_stub2 = + Fabricate( + :external_upload_stub, + status: ExternalUploadStub.statuses[:created], + created_at: (ExternalUploadStub::CREATED_EXPIRY_HOURS.hours + 10.minutes).ago, + ) + external_stub3 = + Fabricate( + :external_upload_stub, + status: ExternalUploadStub.statuses[:uploaded], + created_at: 10.minutes.ago, + ) + external_stub4 = + Fabricate( + :external_upload_stub, + status: ExternalUploadStub.statuses[:uploaded], + created_at: (ExternalUploadStub::UPLOADED_EXPIRY_HOURS.hours + 10.minutes).ago, + ) Jobs::CleanUpUploads.new.execute(nil) expect(ExternalUploadStub.pluck(:id)).to contain_exactly(external_stub1.id, external_stub3.id) end diff --git a/spec/jobs/clean_up_user_export_topics_spec.rb b/spec/jobs/clean_up_user_export_topics_spec.rb index 3bf13967d8d..e26d612ad5b 100644 --- a/spec/jobs/clean_up_user_export_topics_spec.rb +++ b/spec/jobs/clean_up_user_export_topics_spec.rb @@ -3,27 +3,29 @@ RSpec.describe Jobs::CleanUpUserExportTopics do fab!(:user) { Fabricate(:user) } - it 'should delete ancient user export system messages' do - post_en = SystemMessage.create_from_system_user( - user, - :csv_export_succeeded, - download_link: "http://example.com/download", - file_name: "xyz_en.gz", - file_size: "55", - export_title: "user_archive" - ) + it "should delete ancient user export system messages" do + post_en = + SystemMessage.create_from_system_user( + user, + :csv_export_succeeded, + download_link: "http://example.com/download", + file_name: "xyz_en.gz", + file_size: "55", + export_title: "user_archive", + ) topic_en = post_en.topic topic_en.update!(created_at: 5.days.ago) I18n.locale = :fr - post_fr = SystemMessage.create_from_system_user( - user, - :csv_export_succeeded, - download_link: "http://example.com/download", - file_name: "xyz_fr.gz", - file_size: "56", - export_title: "user_archive" - ) + post_fr = + SystemMessage.create_from_system_user( + user, + :csv_export_succeeded, + download_link: "http://example.com/download", + file_name: "xyz_fr.gz", + file_size: "56", + export_title: "user_archive", + ) topic_fr = post_fr.topic topic_fr.update!(created_at: 5.days.ago) diff --git a/spec/jobs/close_topic_spec.rb b/spec/jobs/close_topic_spec.rb index a3b24a82fd3..fd7476897a0 100644 --- a/spec/jobs/close_topic_spec.rb +++ b/spec/jobs/close_topic_spec.rb @@ -3,22 +3,15 @@ RSpec.describe Jobs::CloseTopic do fab!(:admin) { Fabricate(:admin) } - fab!(:topic) do - Fabricate(:topic_timer, user: admin).topic - end + fab!(:topic) { Fabricate(:topic_timer, user: admin).topic } - it 'should be able to close a topic' do + it "should be able to close a topic" do freeze_time(61.minutes.from_now) do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) expect(topic.reload.closed).to eq(true) - expect(Post.last.raw).to eq(I18n.t( - 'topic_statuses.autoclosed_enabled_minutes', count: 61 - )) + expect(Post.last.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_minutes", count: 61)) end end @@ -30,61 +23,47 @@ RSpec.describe Jobs::CloseTopic do end end - describe 'when trying to close a topic that has already been closed' do - it 'should delete the topic timer' do + describe "when trying to close a topic that has already been closed" do + it "should delete the topic timer" do freeze_time(topic.public_topic_timer.execute_at + 1.minute) topic.update!(closed: true) expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false) end end - describe 'when trying to close a topic that has been deleted' do - it 'should delete the topic timer' do + describe "when trying to close a topic that has been deleted" do + it "should delete the topic timer" do freeze_time(topic.public_topic_timer.execute_at + 1.minute) topic.trash! expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false) end end - describe 'when user is no longer authorized to close topics' do + describe "when user is no longer authorized to close topics" do fab!(:user) { Fabricate(:user) } - fab!(:topic) do - Fabricate(:topic_timer, user: user).topic - end + fab!(:topic) { Fabricate(:topic_timer, user: user).topic } - it 'should destroy the topic timer' do + it "should destroy the topic timer" do freeze_time(topic.public_topic_timer.execute_at + 1.minute) expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false) expect(topic.reload.closed).to eq(false) end it "should reconfigure topic timer if category's topics are set to autoclose" do - category = Fabricate(:category, - auto_close_based_on_last_post: true, - auto_close_hours: 5 - ) + category = Fabricate(:category, auto_close_based_on_last_post: true, auto_close_hours: 5) topic = Fabricate(:topic, category: category) topic.public_topic_timer.update!(user: user) @@ -92,12 +71,10 @@ RSpec.describe Jobs::CloseTopic do freeze_time(topic.public_topic_timer.execute_at + 1.minute) expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) - end.to change { topic.reload.public_topic_timer.user }.from(user).to(Discourse.system_user) - .and change { topic.public_topic_timer.id } + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) + end.to change { topic.reload.public_topic_timer.user }.from(user).to( + Discourse.system_user, + ).and change { topic.public_topic_timer.id } expect(topic.reload.closed).to eq(false) end diff --git a/spec/jobs/correct_missing_dualstack_urls_spec.rb b/spec/jobs/correct_missing_dualstack_urls_spec.rb index 09c35a3abd6..7af8e9ece74 100644 --- a/spec/jobs/correct_missing_dualstack_urls_spec.rb +++ b/spec/jobs/correct_missing_dualstack_urls_spec.rb @@ -1,60 +1,70 @@ # frozen_string_literal: true RSpec.describe Jobs::CorrectMissingDualstackUrls do - it 'corrects the urls' do + it "corrects the urls" do setup_s3 SiteSetting.s3_region = "us-east-1" SiteSetting.s3_upload_bucket = "custom-bucket" # we will only correct for our base_url, random urls will be left alone - expect(Discourse.store.absolute_base_url).to eq('//custom-bucket.s3.dualstack.us-east-1.amazonaws.com') - - current_upload = Upload.create!( - url: '//custom-bucket.s3-us-east-1.amazonaws.com/somewhere/a.png', - original_filename: 'a.png', - filesize: 100, - user_id: -1, + expect(Discourse.store.absolute_base_url).to eq( + "//custom-bucket.s3.dualstack.us-east-1.amazonaws.com", ) - bad_upload = Upload.create!( - url: '//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png', - original_filename: 'a.png', - filesize: 100, - user_id: -1, - ) + current_upload = + Upload.create!( + url: "//custom-bucket.s3-us-east-1.amazonaws.com/somewhere/a.png", + original_filename: "a.png", + filesize: 100, + user_id: -1, + ) - current_optimized = OptimizedImage.create!( - url: '//custom-bucket.s3-us-east-1.amazonaws.com/somewhere/a.png', - filesize: 100, - upload_id: current_upload.id, - width: 100, - height: 100, - sha1: 'xxx', - extension: '.png' - ) + bad_upload = + Upload.create!( + url: "//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png", + original_filename: "a.png", + filesize: 100, + user_id: -1, + ) - bad_optimized = OptimizedImage.create!( - url: '//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png', - filesize: 100, - upload_id: current_upload.id, - width: 110, - height: 100, - sha1: 'xxx', - extension: '.png' - ) + current_optimized = + OptimizedImage.create!( + url: "//custom-bucket.s3-us-east-1.amazonaws.com/somewhere/a.png", + filesize: 100, + upload_id: current_upload.id, + width: 100, + height: 100, + sha1: "xxx", + extension: ".png", + ) + + bad_optimized = + OptimizedImage.create!( + url: "//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png", + filesize: 100, + upload_id: current_upload.id, + width: 110, + height: 100, + sha1: "xxx", + extension: ".png", + ) Jobs::CorrectMissingDualstackUrls.new.execute_onceoff(nil) bad_upload.reload - expect(bad_upload.url).to eq('//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png') + expect(bad_upload.url).to eq("//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png") current_upload.reload - expect(current_upload.url).to eq('//custom-bucket.s3.dualstack.us-east-1.amazonaws.com/somewhere/a.png') + expect(current_upload.url).to eq( + "//custom-bucket.s3.dualstack.us-east-1.amazonaws.com/somewhere/a.png", + ) bad_optimized.reload - expect(bad_optimized.url).to eq('//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png') + expect(bad_optimized.url).to eq("//custom-bucket.s3-us-west-1.amazonaws.com/somewhere/a.png") current_optimized.reload - expect(current_optimized.url).to eq('//custom-bucket.s3.dualstack.us-east-1.amazonaws.com/somewhere/a.png') + expect(current_optimized.url).to eq( + "//custom-bucket.s3.dualstack.us-east-1.amazonaws.com/somewhere/a.png", + ) end end diff --git a/spec/jobs/crawl_topic_link_spec.rb b/spec/jobs/crawl_topic_link_spec.rb index 2ced470cff1..95818fb2341 100644 --- a/spec/jobs/crawl_topic_link_spec.rb +++ b/spec/jobs/crawl_topic_link_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true RSpec.describe Jobs::CrawlTopicLink do - let(:job) { Jobs::CrawlTopicLink.new } it "needs a topic_link_id" do diff --git a/spec/jobs/create_linked_topic_spec.rb b/spec/jobs/create_linked_topic_spec.rb index 95831164691..973492bbcd9 100644 --- a/spec/jobs/create_linked_topic_spec.rb +++ b/spec/jobs/create_linked_topic_spec.rb @@ -5,12 +5,10 @@ RSpec.describe Jobs::CreateLinkedTopic do expect { Jobs::CreateLinkedTopic.new.perform(post_id: 1, sync_exec: true) }.not_to raise_error end - context 'with a post' do + context "with a post" do fab!(:category) { Fabricate(:category) } fab!(:topic) { Fabricate(:topic, category: category) } - fab!(:post) do - Fabricate(:post, topic: topic) - end + fab!(:post) { Fabricate(:post, topic: topic) } fab!(:user_1) { Fabricate(:user) } fab!(:user_2) { Fabricate(:user) } @@ -32,21 +30,40 @@ RSpec.describe Jobs::CreateLinkedTopic do Fabricate(:topic_user, notification_level: muted, topic: topic, user: user_2) end - it 'creates a linked topic' do - small_action_post = Fabricate(:post, topic: topic, post_type: Post.types[:small_action], action_code: "closed.enabled") + it "creates a linked topic" do + small_action_post = + Fabricate( + :post, + topic: topic, + post_type: Post.types[:small_action], + action_code: "closed.enabled", + ) Jobs::CreateLinkedTopic.new.execute(post_id: post.id) raw_title = topic.title topic.reload new_topic = Topic.last linked_topic = new_topic.linked_topic - expect(topic.title).to include(I18n.t("create_linked_topic.topic_title_with_sequence", topic_title: raw_title, count: 1)) - expect(topic.posts.last.raw).to include(I18n.t('create_linked_topic.small_action_post_raw', new_title: "[#{new_topic.title}](#{new_topic.url})")) - expect(new_topic.title).to include(I18n.t("create_linked_topic.topic_title_with_sequence", topic_title: raw_title, count: 2)) + expect(topic.title).to include( + I18n.t("create_linked_topic.topic_title_with_sequence", topic_title: raw_title, count: 1), + ) + expect(topic.posts.last.raw).to include( + I18n.t( + "create_linked_topic.small_action_post_raw", + new_title: "[#{new_topic.title}](#{new_topic.url})", + ), + ) + expect(new_topic.title).to include( + I18n.t("create_linked_topic.topic_title_with_sequence", topic_title: raw_title, count: 2), + ) expect(new_topic.first_post.raw).to include(topic.url) expect(new_topic.category.id).to eq(category.id) expect(new_topic.topic_users.count).to eq(3) - expect(new_topic.topic_users.pluck(:notification_level)).to contain_exactly(muted, tracking, watching) + expect(new_topic.topic_users.pluck(:notification_level)).to contain_exactly( + muted, + tracking, + watching, + ) expect(linked_topic.topic_id).to eq(new_topic.id) expect(linked_topic.original_topic_id).to eq(topic.id) expect(linked_topic.sequence).to eq(2) diff --git a/spec/jobs/create_recent_post_search_indexes_spec.rb b/spec/jobs/create_recent_post_search_indexes_spec.rb index fa7e9a931ed..1d350d254b4 100644 --- a/spec/jobs/create_recent_post_search_indexes_spec.rb +++ b/spec/jobs/create_recent_post_search_indexes_spec.rb @@ -13,21 +13,19 @@ RSpec.describe Jobs::CreateRecentPostSearchIndexes do Fabricate(:post) end - before do - SearchIndexer.enable - end + before { SearchIndexer.enable } - describe '#execute' do - it 'should not create the index if requried posts size has not been reached' do + describe "#execute" do + it "should not create the index if requried posts size has not been reached" do SiteSetting.search_recent_posts_size = 1 SiteSetting.search_enable_recent_regular_posts_offset_size = 3 - expect do - subject.execute({}) - end.to_not change { SiteSetting.search_recent_regular_posts_offset_post_id } + expect do subject.execute({}) end.to_not change { + SiteSetting.search_recent_regular_posts_offset_post_id + } end - it 'should create the right index' do + it "should create the right index" do SiteSetting.search_recent_posts_size = 1 SiteSetting.search_enable_recent_regular_posts_offset_size = 1 diff --git a/spec/jobs/create_user_reviewable_spec.rb b/spec/jobs/create_user_reviewable_spec.rb index 1a9634af94b..3c9a2c2b37b 100644 --- a/spec/jobs/create_user_reviewable_spec.rb +++ b/spec/jobs/create_user_reviewable_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true RSpec.describe Jobs::CreateUserReviewable do - let(:user) { Fabricate(:user) } it "creates the reviewable" do @@ -11,9 +10,9 @@ RSpec.describe Jobs::CreateUserReviewable do reviewable = Reviewable.find_by(target: user) expect(reviewable).to be_present expect(reviewable.pending?).to eq(true) - expect(reviewable.payload['username']).to eq(user.username) - expect(reviewable.payload['name']).to eq(user.name) - expect(reviewable.payload['email']).to eq(user.email) + expect(reviewable.payload["username"]).to eq(user.username) + expect(reviewable.payload["name"]).to eq(user.name) + expect(reviewable.payload["email"]).to eq(user.email) end it "should not raise an error if there is a reviewable already" do @@ -37,7 +36,7 @@ RSpec.describe Jobs::CreateUserReviewable do reviewable = Reviewable.find_by(target: user) score = reviewable.reviewable_scores.first expect(score).to be_present - expect(score.reason).to eq('must_approve_users') + expect(score.reason).to eq("must_approve_users") end it "adds invite_only if enabled" do @@ -46,8 +45,7 @@ RSpec.describe Jobs::CreateUserReviewable do reviewable = Reviewable.find_by(target: user) score = reviewable.reviewable_scores.first expect(score).to be_present - expect(score.reason).to eq('invite_only') + expect(score.reason).to eq("invite_only") end end - end diff --git a/spec/jobs/dashboard_stats_spec.rb b/spec/jobs/dashboard_stats_spec.rb index 3f2304db9cb..82aeaab1223 100644 --- a/spec/jobs/dashboard_stats_spec.rb +++ b/spec/jobs/dashboard_stats_spec.rb @@ -1,18 +1,18 @@ # frozen_string_literal: true RSpec.describe ::Jobs::DashboardStats do - let(:group_message) { GroupMessage.new(Group[:admins].name, :dashboard_problems, limit_once_per: 7.days.to_i) } + let(:group_message) do + GroupMessage.new(Group[:admins].name, :dashboard_problems, limit_once_per: 7.days.to_i) + end def clear_recently_sent! # We won't immediately create new PMs due to the limit_once_per option, reset the value for testing purposes. Discourse.redis.del(group_message.sent_recently_key) end - after do - clear_recently_sent! - end + after { clear_recently_sent! } - it 'creates group message when problems are persistent for 2 days' do + it "creates group message when problems are persistent for 2 days" do Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, Time.zone.now.to_s) expect { described_class.new.execute({}) }.not_to change { Topic.count } @@ -20,7 +20,7 @@ RSpec.describe ::Jobs::DashboardStats do expect { described_class.new.execute({}) }.to change { Topic.count }.by(1) end - it 'replaces old message' do + it "replaces old message" do Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago) expect { described_class.new.execute({}) }.to change { Topic.count }.by(1) old_topic = Topic.last @@ -32,14 +32,14 @@ RSpec.describe ::Jobs::DashboardStats do expect(new_topic.title).to eq(old_topic.title) end - it 'respects the sent_recently? check when deleting previous message' do + it "respects the sent_recently? check when deleting previous message" do Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago) expect { described_class.new.execute({}) }.to change { Topic.count }.by(1) expect { described_class.new.execute({}) }.not_to change { Topic.count } end - it 'duplicates message if previous one has replies' do + it "duplicates message if previous one has replies" do Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago) expect { described_class.new.execute({}) }.to change { Topic.count }.by(1) clear_recently_sent! @@ -48,7 +48,7 @@ RSpec.describe ::Jobs::DashboardStats do expect { described_class.new.execute({}) }.to change { Topic.count }.by(1) end - it 'duplicates message if previous was 3 months ago' do + it "duplicates message if previous was 3 months ago" do freeze_time 3.months.ago do Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago) expect { described_class.new.execute({}) }.to change { Topic.count }.by(1) diff --git a/spec/jobs/delete_replies_spec.rb b/spec/jobs/delete_replies_spec.rb index 7bd9e219db9..cab17ca543d 100644 --- a/spec/jobs/delete_replies_spec.rb +++ b/spec/jobs/delete_replies_spec.rb @@ -5,21 +5,26 @@ RSpec.describe Jobs::DeleteReplies do fab!(:topic) { Fabricate(:topic) } fab!(:topic_timer) do - Fabricate(:topic_timer, status_type: TopicTimer.types[:delete_replies], duration_minutes: 2880, user: admin, topic: topic, execute_at: 2.days.from_now) + Fabricate( + :topic_timer, + status_type: TopicTimer.types[:delete_replies], + duration_minutes: 2880, + user: admin, + topic: topic, + execute_at: 2.days.from_now, + ) end - before do - 3.times { create_post(topic: topic) } - end + before { 3.times { create_post(topic: topic) } } it "can delete replies of a topic" do SiteSetting.skip_auto_delete_reply_likes = 0 freeze_time (2.days.from_now) - expect { - described_class.new.execute(topic_timer_id: topic_timer.id) - }.to change { topic.posts.count }.by(-2) + expect { described_class.new.execute(topic_timer_id: topic_timer.id) }.to change { + topic.posts.count + }.by(-2) topic_timer.reload expect(topic_timer.execute_at).to eq_time(2.day.from_now) @@ -32,8 +37,8 @@ RSpec.describe Jobs::DeleteReplies do topic.posts.last.update!(like_count: SiteSetting.skip_auto_delete_reply_likes + 1) - expect { - described_class.new.execute(topic_timer_id: topic_timer.id) - }.to change { topic.posts.count }.by(-1) + expect { described_class.new.execute(topic_timer_id: topic_timer.id) }.to change { + topic.posts.count + }.by(-1) end end diff --git a/spec/jobs/delete_topic_spec.rb b/spec/jobs/delete_topic_spec.rb index a65fb3052b8..18ce649fdb0 100644 --- a/spec/jobs/delete_topic_spec.rb +++ b/spec/jobs/delete_topic_spec.rb @@ -3,9 +3,7 @@ RSpec.describe Jobs::DeleteTopic do fab!(:admin) { Fabricate(:admin) } - fab!(:topic) do - Fabricate(:topic_timer, user: admin).topic - end + fab!(:topic) { Fabricate(:topic_timer, user: admin).topic } let(:first_post) { create_post(topic: topic) } @@ -18,7 +16,6 @@ RSpec.describe Jobs::DeleteTopic do expect(topic.reload).to be_trashed expect(first_post.reload).to be_trashed expect(topic.reload.public_topic_timer).to eq(nil) - end it "should do nothing if topic is already deleted" do @@ -42,9 +39,7 @@ RSpec.describe Jobs::DeleteTopic do end describe "user isn't authorized to delete topics" do - let(:topic) { - Fabricate(:topic_timer, user: Fabricate(:user)).topic - } + let(:topic) { Fabricate(:topic_timer, user: Fabricate(:user)).topic } it "shouldn't delete the topic" do create_post(topic: topic) @@ -55,5 +50,4 @@ RSpec.describe Jobs::DeleteTopic do expect(topic.reload).to_not be_trashed end end - end diff --git a/spec/jobs/disable_bootstrap_mode_spec.rb b/spec/jobs/disable_bootstrap_mode_spec.rb index 3d5b8d4e13c..9b53e49b28a 100644 --- a/spec/jobs/disable_bootstrap_mode_spec.rb +++ b/spec/jobs/disable_bootstrap_mode_spec.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true RSpec.describe Jobs::DisableBootstrapMode do - - describe '.execute' do + describe ".execute" do fab!(:admin) { Fabricate(:admin) } before do @@ -11,30 +10,28 @@ RSpec.describe Jobs::DisableBootstrapMode do SiteSetting.default_email_digest_frequency = 1440 end - it 'does not execute if bootstrap mode is already disabled' do + it "does not execute if bootstrap mode is already disabled" do SiteSetting.bootstrap_mode_enabled = false StaffActionLogger.any_instance.expects(:log_site_setting_change).never Jobs::DisableBootstrapMode.new.execute(user_id: admin.id) end - it 'turns off bootstrap mode if bootstrap_mode_min_users is set to 0' do + it "turns off bootstrap mode if bootstrap_mode_min_users is set to 0" do SiteSetting.bootstrap_mode_min_users = 0 StaffActionLogger.any_instance.expects(:log_site_setting_change).times(3) Jobs::DisableBootstrapMode.new.execute(user_id: admin.id) end - it 'does not amend setting that is not in bootstrap state' do + it "does not amend setting that is not in bootstrap state" do SiteSetting.bootstrap_mode_min_users = 0 SiteSetting.default_trust_level = TrustLevel[3] StaffActionLogger.any_instance.expects(:log_site_setting_change).twice Jobs::DisableBootstrapMode.new.execute(user_id: admin.id) end - it 'successfully turns off bootstrap mode' do + it "successfully turns off bootstrap mode" do SiteSetting.bootstrap_mode_min_users = 5 - 6.times do - Fabricate(:user) - end + 6.times { Fabricate(:user) } StaffActionLogger.any_instance.expects(:log_site_setting_change).times(3) Jobs::DisableBootstrapMode.new.execute(user_id: admin.id) end diff --git a/spec/jobs/download_avatar_from_url_spec.rb b/spec/jobs/download_avatar_from_url_spec.rb index 36ab8d446de..a3c36d257fe 100644 --- a/spec/jobs/download_avatar_from_url_spec.rb +++ b/spec/jobs/download_avatar_from_url_spec.rb @@ -3,13 +3,10 @@ RSpec.describe Jobs::DownloadAvatarFromUrl do fab!(:user) { Fabricate(:user) } - describe 'when url is invalid' do - it 'should not raise any error' do + describe "when url is invalid" do + it "should not raise any error" do expect do - described_class.new.execute( - url: '/assets/something/nice.jpg', - user_id: user.id - ) + described_class.new.execute(url: "/assets/something/nice.jpg", user_id: user.id) end.to_not raise_error end end diff --git a/spec/jobs/download_backup_email_spec.rb b/spec/jobs/download_backup_email_spec.rb index 9b3325af2f3..efafdfba60f 100644 --- a/spec/jobs/download_backup_email_spec.rb +++ b/spec/jobs/download_backup_email_spec.rb @@ -4,17 +4,16 @@ RSpec.describe Jobs::DownloadBackupEmail do fab!(:user) { Fabricate(:admin) } it "should work" do - described_class.new.execute( - user_id: user.id, - backup_file_path: "http://some.example.test/" - ) + described_class.new.execute(user_id: user.id, backup_file_path: "http://some.example.test/") email = ActionMailer::Base.deliveries.last - expect(email.subject).to eq(I18n.t('download_backup_mailer.subject_template', - email_prefix: SiteSetting.title - )) + expect(email.subject).to eq( + I18n.t("download_backup_mailer.subject_template", email_prefix: SiteSetting.title), + ) - expect(email.body.raw_source).to include("http://some.example.test/?token=#{EmailBackupToken.get(user.id)}") + expect(email.body.raw_source).to include( + "http://some.example.test/?token=#{EmailBackupToken.get(user.id)}", + ) end end diff --git a/spec/jobs/download_profile_background_from_url_spec.rb b/spec/jobs/download_profile_background_from_url_spec.rb index 1870aa10626..0fbf682a848 100644 --- a/spec/jobs/download_profile_background_from_url_spec.rb +++ b/spec/jobs/download_profile_background_from_url_spec.rb @@ -3,13 +3,10 @@ RSpec.describe Jobs::DownloadProfileBackgroundFromUrl do fab!(:user) { Fabricate(:user) } - describe 'when url is invalid' do - it 'should not raise any error' do + describe "when url is invalid" do + it "should not raise any error" do expect do - described_class.new.execute( - url: '/assets/something/nice.jpg', - user_id: user.id - ) + described_class.new.execute(url: "/assets/something/nice.jpg", user_id: user.id) end.to_not raise_error end end diff --git a/spec/jobs/emit_web_hook_event_spec.rb b/spec/jobs/emit_web_hook_event_spec.rb index 5abe40e9967..efddb33a040 100644 --- a/spec/jobs/emit_web_hook_event_spec.rb +++ b/spec/jobs/emit_web_hook_event_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'excon' +require "excon" RSpec.describe Jobs::EmitWebHookEvent do fab!(:post_hook) { Fabricate(:web_hook) } @@ -8,22 +8,22 @@ RSpec.describe Jobs::EmitWebHookEvent do fab!(:post) { Fabricate(:post) } fab!(:user) { Fabricate(:user) } - it 'raises an error when there is no web hook record' do - expect do - subject.execute(event_type: 'post', payload: {}) - end.to raise_error(Discourse::InvalidParameters) + it "raises an error when there is no web hook record" do + expect do subject.execute(event_type: "post", payload: {}) end.to raise_error( + Discourse::InvalidParameters, + ) end - it 'raises an error when there is no event type' do - expect do - subject.execute(web_hook_id: post_hook.id, payload: {}) - end.to raise_error(Discourse::InvalidParameters) + it "raises an error when there is no event type" do + expect do subject.execute(web_hook_id: post_hook.id, payload: {}) end.to raise_error( + Discourse::InvalidParameters, + ) end - it 'raises an error when there is no payload' do - expect do - subject.execute(web_hook_id: post_hook.id, event_type: 'post') - end.to raise_error(Discourse::InvalidParameters) + it "raises an error when there is no payload" do + expect do subject.execute(web_hook_id: post_hook.id, event_type: "post") end.to raise_error( + Discourse::InvalidParameters, + ) end it "should not destroy webhook event in case of error" do @@ -32,139 +32,127 @@ RSpec.describe Jobs::EmitWebHookEvent do subject.execute( web_hook_id: post_hook.id, payload: { id: post.id }.to_json, - event_type: WebHookEventType::POST + event_type: WebHookEventType::POST, ) expect(WebHookEvent.last.web_hook_id).to eq(post_hook.id) end - context 'when the web hook is failed' do - before do - SiteSetting.retry_web_hook_events = true - end + context "when the web hook is failed" do + before { SiteSetting.retry_web_hook_events = true } - context 'when the webhook has failed for 404 or 410' do + context "when the webhook has failed for 404 or 410" do before do - stub_request(:post, post_hook.payload_url).to_return(body: 'Invalid Access', status: response_status) + stub_request(:post, post_hook.payload_url).to_return( + body: "Invalid Access", + status: response_status, + ) end let(:response_status) { 410 } - it 'disables the webhook' do + it "disables the webhook" do expect do subject.execute( web_hook_id: post_hook.id, event_type: described_class::PING_EVENT, - retry_count: described_class::MAX_RETRY_COUNT + retry_count: described_class::MAX_RETRY_COUNT, ) end.to change { post_hook.reload.active }.to(false) end - it 'logs webhook deactivation reason' do + it "logs webhook deactivation reason" do subject.execute( web_hook_id: post_hook.id, event_type: described_class::PING_EVENT, - retry_count: described_class::MAX_RETRY_COUNT + retry_count: described_class::MAX_RETRY_COUNT, ) - user_history = UserHistory.find_by(action: UserHistory.actions[:web_hook_deactivate], acting_user: Discourse.system_user) + user_history = + UserHistory.find_by( + action: UserHistory.actions[:web_hook_deactivate], + acting_user: Discourse.system_user, + ) expect(user_history).to be_present - expect(user_history.context).to eq([ - "webhook_id: #{post_hook.id}", - "webhook_response_status: #{response_status}" - ].to_s) + expect(user_history.context).to eq( + ["webhook_id: #{post_hook.id}", "webhook_response_status: #{response_status}"].to_s, + ) end end - context 'when the webhook has failed' do + context "when the webhook has failed" do before do - stub_request(:post, post_hook.payload_url).to_return(body: 'Invalid Access', status: 403) + stub_request(:post, post_hook.payload_url).to_return(body: "Invalid Access", status: 403) end - it 'retry if site setting is enabled' do + it "retry if site setting is enabled" do expect do - subject.execute( - web_hook_id: post_hook.id, - event_type: described_class::PING_EVENT - ) + subject.execute(web_hook_id: post_hook.id, event_type: described_class::PING_EVENT) end.to change { Jobs::EmitWebHookEvent.jobs.size }.by(1) end - it 'retries at most 5 times' do + it "retries at most 5 times" do Jobs.run_immediately! expect(Jobs::EmitWebHookEvent::MAX_RETRY_COUNT + 1).to eq(5) expect do - subject.execute( - web_hook_id: post_hook.id, - event_type: described_class::PING_EVENT - ) + subject.execute(web_hook_id: post_hook.id, event_type: described_class::PING_EVENT) end.to change { WebHookEvent.count }.by(Jobs::EmitWebHookEvent::MAX_RETRY_COUNT + 1) end - it 'does not retry for more than maximum allowed times' do + it "does not retry for more than maximum allowed times" do expect do subject.execute( web_hook_id: post_hook.id, event_type: described_class::PING_EVENT, - retry_count: described_class::MAX_RETRY_COUNT + retry_count: described_class::MAX_RETRY_COUNT, ) end.to_not change { Jobs::EmitWebHookEvent.jobs.size } end - it 'does not retry if site setting is disabled' do + it "does not retry if site setting is disabled" do SiteSetting.retry_web_hook_events = false expect do - subject.execute( - web_hook_id: post_hook.id, - event_type: described_class::PING_EVENT - ) + subject.execute(web_hook_id: post_hook.id, event_type: described_class::PING_EVENT) end.not_to change { Jobs::EmitWebHookEvent.jobs.size } end - it 'properly logs error on rescue' do + it "properly logs error on rescue" do stub_request(:post, post_hook.payload_url).to_raise("connection error") - subject.execute( - web_hook_id: post_hook.id, - event_type: described_class::PING_EVENT - ) + subject.execute(web_hook_id: post_hook.id, event_type: described_class::PING_EVENT) event = WebHookEvent.last - expect(event.payload).to eq(MultiJson.dump(ping: 'OK')) + expect(event.payload).to eq(MultiJson.dump(ping: "OK")) expect(event.status).to eq(-1) - expect(MultiJson.load(event.response_headers)['error']).to eq('connection error') + expect(MultiJson.load(event.response_headers)["error"]).to eq("connection error") end end end - it 'does not raise an error for a ping event without payload' do - stub_request(:post, post_hook.payload_url) - .to_return(body: 'OK', status: 200) + it "does not raise an error for a ping event without payload" do + stub_request(:post, post_hook.payload_url).to_return(body: "OK", status: 200) - subject.execute( - web_hook_id: post_hook.id, - event_type: described_class::PING_EVENT - ) + subject.execute(web_hook_id: post_hook.id, event_type: described_class::PING_EVENT) end it "doesn't emit when the hook is inactive" do subject.execute( web_hook_id: inactive_hook.id, - event_type: 'post', - payload: { test: "some payload" }.to_json + event_type: "post", + payload: { test: "some payload" }.to_json, ) end - it 'emits normally with sufficient arguments' do - stub_request(:post, post_hook.payload_url) - .with(body: "{\"post\":{\"test\":\"some payload\"}}") - .to_return(body: 'OK', status: 200) + it "emits normally with sufficient arguments" do + stub_request(:post, post_hook.payload_url).with( + body: "{\"post\":{\"test\":\"some payload\"}}", + ).to_return(body: "OK", status: 200) subject.execute( web_hook_id: post_hook.id, - event_type: 'post', - payload: { test: "some payload" }.to_json + event_type: "post", + payload: { test: "some payload" }.to_json, ) end @@ -172,17 +160,19 @@ RSpec.describe Jobs::EmitWebHookEvent do FinalDestination::TestHelper.stub_to_fail do subject.execute( web_hook_id: post_hook.id, - event_type: 'post', - payload: { test: "some payload" }.to_json + event_type: "post", + payload: { test: "some payload" }.to_json, ) end event = post_hook.web_hook_events.last - expect(event.response_headers).to eq({ error: I18n.t("webhooks.payload_url.blocked_or_internal") }.to_json) + expect(event.response_headers).to eq( + { error: I18n.t("webhooks.payload_url.blocked_or_internal") }.to_json, + ) expect(event.response_body).to eq(nil) expect(event.status).to eq(-1) end - context 'with category filters' do + context "with category filters" do fab!(:category) { Fabricate(:category) } fab!(:topic) { Fabricate(:topic) } fab!(:topic_with_category) { Fabricate(:topic, category_id: category.id) } @@ -191,27 +181,27 @@ RSpec.describe Jobs::EmitWebHookEvent do it "doesn't emit when event is not related with defined categories" do subject.execute( web_hook_id: topic_hook.id, - event_type: 'topic', + event_type: "topic", category_id: topic.category.id, - payload: { test: "some payload" }.to_json + payload: { test: "some payload" }.to_json, ) end - it 'emit when event is related with defined categories' do - stub_request(:post, post_hook.payload_url) - .with(body: "{\"topic\":{\"test\":\"some payload\"}}") - .to_return(body: 'OK', status: 200) + it "emit when event is related with defined categories" do + stub_request(:post, post_hook.payload_url).with( + body: "{\"topic\":{\"test\":\"some payload\"}}", + ).to_return(body: "OK", status: 200) subject.execute( web_hook_id: topic_hook.id, - event_type: 'topic', + event_type: "topic", category_id: topic_with_category.category.id, - payload: { test: "some payload" }.to_json + payload: { test: "some payload" }.to_json, ) end end - context 'with tag filters' do + context "with tag filters" do fab!(:tag) { Fabricate(:tag) } fab!(:topic) { Fabricate(:topic, tags: [tag]) } fab!(:topic_hook) { Fabricate(:topic_web_hook, tags: [tag]) } @@ -219,35 +209,35 @@ RSpec.describe Jobs::EmitWebHookEvent do it "doesn't emit when event is not included any tags" do subject.execute( web_hook_id: topic_hook.id, - event_type: 'topic', - payload: { test: "some payload" }.to_json + event_type: "topic", + payload: { test: "some payload" }.to_json, ) end it "doesn't emit when event is not related with defined tags" do subject.execute( web_hook_id: topic_hook.id, - event_type: 'topic', + event_type: "topic", tag_ids: [Fabricate(:tag).id], - payload: { test: "some payload" }.to_json + payload: { test: "some payload" }.to_json, ) end - it 'emit when event is related with defined tags' do - stub_request(:post, post_hook.payload_url) - .with(body: "{\"topic\":{\"test\":\"some payload\"}}") - .to_return(body: 'OK', status: 200) + it "emit when event is related with defined tags" do + stub_request(:post, post_hook.payload_url).with( + body: "{\"topic\":{\"test\":\"some payload\"}}", + ).to_return(body: "OK", status: 200) subject.execute( web_hook_id: topic_hook.id, - event_type: 'topic', + event_type: "topic", tag_ids: topic.tags.pluck(:id), - payload: { test: "some payload" }.to_json + payload: { test: "some payload" }.to_json, ) end end - context 'with group filters' do + context "with group filters" do fab!(:group) { Fabricate(:group) } fab!(:user) { Fabricate(:user, groups: [group]) } fab!(:like_hook) { Fabricate(:like_web_hook, groups: [group]) } @@ -255,38 +245,37 @@ RSpec.describe Jobs::EmitWebHookEvent do it "doesn't emit when event is not included any groups" do subject.execute( web_hook_id: like_hook.id, - event_type: 'like', - payload: { test: "some payload" }.to_json + event_type: "like", + payload: { test: "some payload" }.to_json, ) end it "doesn't emit when event is not related with defined groups" do subject.execute( web_hook_id: like_hook.id, - event_type: 'like', + event_type: "like", group_ids: [Fabricate(:group).id], - payload: { test: "some payload" }.to_json + payload: { test: "some payload" }.to_json, ) end - it 'emit when event is related with defined groups' do - stub_request(:post, like_hook.payload_url) - .with(body: "{\"like\":{\"test\":\"some payload\"}}") - .to_return(body: 'OK', status: 200) + it "emit when event is related with defined groups" do + stub_request(:post, like_hook.payload_url).with( + body: "{\"like\":{\"test\":\"some payload\"}}", + ).to_return(body: "OK", status: 200) subject.execute( web_hook_id: like_hook.id, - event_type: 'like', + event_type: "like", group_ids: user.groups.pluck(:id), - payload: { test: "some payload" }.to_json + payload: { test: "some payload" }.to_json, ) end end - describe '#send_webhook!' do - it 'creates delivery event record' do - stub_request(:post, post_hook.payload_url) - .to_return(body: 'OK', status: 200) + describe "#send_webhook!" do + it "creates delivery event record" do + stub_request(:post, post_hook.payload_url).to_return(body: "OK", status: 200) topic_event_type = WebHookEventType.all.first web_hook_id = Fabricate("#{topic_event_type.name}_web_hook").id @@ -295,55 +284,64 @@ RSpec.describe Jobs::EmitWebHookEvent do subject.execute( web_hook_id: web_hook_id, event_type: topic_event_type.name, - payload: { test: "some payload" }.to_json + payload: { test: "some payload" }.to_json, ) end.to change(WebHookEvent, :count).by(1) end - it 'sets up proper request headers' do - stub_request(:post, post_hook.payload_url) - .to_return(headers: { test: 'string' }, body: 'OK', status: 200) + it "sets up proper request headers" do + stub_request(:post, post_hook.payload_url).to_return( + headers: { + test: "string", + }, + body: "OK", + status: 200, + ) subject.execute( web_hook_id: post_hook.id, event_type: described_class::PING_EVENT, event_name: described_class::PING_EVENT, - payload: { test: "this payload shouldn't appear" }.to_json + payload: { test: "this payload shouldn't appear" }.to_json, ) event = WebHookEvent.last headers = MultiJson.load(event.headers) - expect(headers['Content-Length']).to eq("13") - expect(headers['Host']).to eq("meta.discourse.org") - expect(headers['X-Discourse-Event-Id']).to eq(event.id.to_s) - expect(headers['X-Discourse-Event-Type']).to eq(described_class::PING_EVENT) - expect(headers['X-Discourse-Event']).to eq(described_class::PING_EVENT) - expect(headers['X-Discourse-Event-Signature']).to eq('sha256=162f107f6b5022353274eb1a7197885cfd35744d8d08e5bcea025d309386b7d6') - expect(event.payload).to eq(MultiJson.dump(ping: 'OK')) + expect(headers["Content-Length"]).to eq("13") + expect(headers["Host"]).to eq("meta.discourse.org") + expect(headers["X-Discourse-Event-Id"]).to eq(event.id.to_s) + expect(headers["X-Discourse-Event-Type"]).to eq(described_class::PING_EVENT) + expect(headers["X-Discourse-Event"]).to eq(described_class::PING_EVENT) + expect(headers["X-Discourse-Event-Signature"]).to eq( + "sha256=162f107f6b5022353274eb1a7197885cfd35744d8d08e5bcea025d309386b7d6", + ) + expect(event.payload).to eq(MultiJson.dump(ping: "OK")) expect(event.status).to eq(200) - expect(MultiJson.load(event.response_headers)['test']).to eq('string') - expect(event.response_body).to eq('OK') + expect(MultiJson.load(event.response_headers)["test"]).to eq("string") + expect(event.response_body).to eq("OK") end - it 'sets up proper request headers when an error raised' do + it "sets up proper request headers when an error raised" do stub_request(:post, post_hook.payload_url).to_raise("error") subject.execute( web_hook_id: post_hook.id, event_type: described_class::PING_EVENT, event_name: described_class::PING_EVENT, - payload: { test: "this payload shouldn't appear" }.to_json + payload: { test: "this payload shouldn't appear" }.to_json, ) event = WebHookEvent.last headers = MultiJson.load(event.headers) - expect(headers['Content-Length']).to eq("13") - expect(headers['Host']).to eq("meta.discourse.org") - expect(headers['X-Discourse-Event-Id']).to eq(event.id.to_s) - expect(headers['X-Discourse-Event-Type']).to eq(described_class::PING_EVENT) - expect(headers['X-Discourse-Event']).to eq(described_class::PING_EVENT) - expect(headers['X-Discourse-Event-Signature']).to eq('sha256=162f107f6b5022353274eb1a7197885cfd35744d8d08e5bcea025d309386b7d6') - expect(event.payload).to eq(MultiJson.dump(ping: 'OK')) + expect(headers["Content-Length"]).to eq("13") + expect(headers["Host"]).to eq("meta.discourse.org") + expect(headers["X-Discourse-Event-Id"]).to eq(event.id.to_s) + expect(headers["X-Discourse-Event-Type"]).to eq(described_class::PING_EVENT) + expect(headers["X-Discourse-Event"]).to eq(described_class::PING_EVENT) + expect(headers["X-Discourse-Event-Signature"]).to eq( + "sha256=162f107f6b5022353274eb1a7197885cfd35744d8d08e5bcea025d309386b7d6", + ) + expect(event.payload).to eq(MultiJson.dump(ping: "OK")) end end end diff --git a/spec/jobs/enable_bootstrap_mode_spec.rb b/spec/jobs/enable_bootstrap_mode_spec.rb index 583caa041e7..faaa5f82155 100644 --- a/spec/jobs/enable_bootstrap_mode_spec.rb +++ b/spec/jobs/enable_bootstrap_mode_spec.rb @@ -1,37 +1,36 @@ # frozen_string_literal: true RSpec.describe Jobs::EnableBootstrapMode do - - describe '.execute' do + describe ".execute" do fab!(:admin) { Fabricate(:admin) } - before do - SiteSetting.bootstrap_mode_enabled = false + before { SiteSetting.bootstrap_mode_enabled = false } + + it "raises an error when user_id is missing" do + expect { Jobs::EnableBootstrapMode.new.execute({}) }.to raise_error( + Discourse::InvalidParameters, + ) end - it 'raises an error when user_id is missing' do - expect { Jobs::EnableBootstrapMode.new.execute({}) }.to raise_error(Discourse::InvalidParameters) - end - - it 'does not execute if bootstrap mode is already enabled' do + it "does not execute if bootstrap mode is already enabled" do SiteSetting.bootstrap_mode_enabled = true StaffActionLogger.any_instance.expects(:log_site_setting_change).never Jobs::EnableBootstrapMode.new.execute(user_id: admin.id) end - it 'does not turn on bootstrap mode if first admin already exists' do + it "does not turn on bootstrap mode if first admin already exists" do first_admin = Fabricate(:admin) StaffActionLogger.any_instance.expects(:log_site_setting_change).never Jobs::EnableBootstrapMode.new.execute(user_id: admin.id) end - it 'does not amend setting that is not in default state' do + it "does not amend setting that is not in default state" do SiteSetting.default_trust_level = TrustLevel[3] StaffActionLogger.any_instance.expects(:log_site_setting_change).twice Jobs::EnableBootstrapMode.new.execute(user_id: admin.id) end - it 'successfully turns on bootstrap mode' do + it "successfully turns on bootstrap mode" do StaffActionLogger.any_instance.expects(:log_site_setting_change).times(3) Jobs::EnableBootstrapMode.new.execute(user_id: admin.id) end diff --git a/spec/jobs/enqueue_digest_emails_spec.rb b/spec/jobs/enqueue_digest_emails_spec.rb index 82b18c8c646..681060b9a60 100644 --- a/spec/jobs/enqueue_digest_emails_spec.rb +++ b/spec/jobs/enqueue_digest_emails_spec.rb @@ -1,54 +1,74 @@ # frozen_string_literal: true RSpec.describe Jobs::EnqueueDigestEmails do - - describe '#target_users' do - context 'with disabled digests' do + describe "#target_users" do + context "with disabled digests" do before { SiteSetting.default_email_digest_frequency = 0 } - let!(:user_no_digests) { Fabricate(:active_user, last_emailed_at: 8.days.ago, last_seen_at: 10.days.ago) } + let!(:user_no_digests) do + Fabricate(:active_user, last_emailed_at: 8.days.ago, last_seen_at: 10.days.ago) + end it "doesn't return users with email disabled" do - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_no_digests.id)).to eq(false) + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_no_digests.id)).to eq( + false, + ) end end - context 'with unapproved users' do - before do - SiteSetting.must_approve_users = true + context "with unapproved users" do + before { SiteSetting.must_approve_users = true } + + let!(:unapproved_user) do + Fabricate( + :active_user, + approved: false, + last_emailed_at: 8.days.ago, + last_seen_at: 10.days.ago, + ) end - let!(:unapproved_user) { Fabricate(:active_user, approved: false, last_emailed_at: 8.days.ago, last_seen_at: 10.days.ago) } - - it 'should enqueue the right digest emails' do - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq(false) + it "should enqueue the right digest emails" do + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq( + false, + ) # As a moderator unapproved_user.update_column(:moderator, true) - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq(true) + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq( + true, + ) # As an admin unapproved_user.update(admin: true, moderator: false) - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq(true) + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq( + true, + ) # As an approved user unapproved_user.update(admin: false, moderator: false, approved: true) - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq(true) + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(unapproved_user.id)).to eq( + true, + ) end end - context 'with staged users' do - let!(:staged_user) { Fabricate(:active_user, staged: true, last_emailed_at: 1.year.ago, last_seen_at: 1.year.ago) } + context "with staged users" do + let!(:staged_user) do + Fabricate(:active_user, staged: true, last_emailed_at: 1.year.ago, last_seen_at: 1.year.ago) + end it "doesn't return staged users" do expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(staged_user.id)).to eq(false) end end - context 'when recently emailed' do + context "when recently emailed" do let!(:user_emailed_recently) { Fabricate(:active_user, last_emailed_at: 6.days.ago) } it "doesn't return users who have been emailed recently" do - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_emailed_recently.id)).to eq(false) + expect( + Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_emailed_recently.id), + ).to eq(false) end end @@ -56,19 +76,25 @@ RSpec.describe Jobs::EnqueueDigestEmails do let!(:inactive_user) { Fabricate(:user, active: false) } it "doesn't return users who have been emailed recently" do - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(inactive_user.id)).to eq(false) + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(inactive_user.id)).to eq( + false, + ) end end context "with suspended user" do - let!(:suspended_user) { Fabricate(:user, suspended_till: 1.week.from_now, suspended_at: 1.day.ago) } + let!(:suspended_user) do + Fabricate(:user, suspended_till: 1.week.from_now, suspended_at: 1.day.ago) + end it "doesn't return users who are suspended" do - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(suspended_user.id)).to eq(false) + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(suspended_user.id)).to eq( + false, + ) end end - context 'when visited the site this week' do + context "when visited the site this week" do let(:user_visited_this_week) { Fabricate(:active_user, last_seen_at: 6.days.ago) } it "doesn't return users who have been emailed recently" do @@ -77,23 +103,30 @@ RSpec.describe Jobs::EnqueueDigestEmails do end end - context 'when visited the site a year ago' do + context "when visited the site a year ago" do let!(:user_visited_a_year_ago) { Fabricate(:active_user, last_seen_at: 370.days.ago) } it "doesn't return the user who have not visited the site for more than 365 days" do - expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_visited_a_year_ago.id)).to eq(false) + expect( + Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_visited_a_year_ago.id), + ).to eq(false) end end - context 'with regular users' do - let!(:user) { Fabricate(:active_user, last_seen_at: (SiteSetting.suppress_digest_email_after_days - 1).days.ago) } + context "with regular users" do + let!(:user) do + Fabricate( + :active_user, + last_seen_at: (SiteSetting.suppress_digest_email_after_days - 1).days.ago, + ) + end it "returns the user" do expect(Jobs::EnqueueDigestEmails.new.target_user_ids).to eq([user.id]) end end - context 'with too many bounces' do + context "with too many bounces" do let!(:bounce_user) { Fabricate(:active_user, last_seen_at: 6.month.ago) } it "doesn't return users with too many bounces" do @@ -112,7 +145,7 @@ RSpec.describe Jobs::EnqueueDigestEmails do end end - describe '#execute' do + describe "#execute" do let(:user) { Fabricate(:user) } it "limits jobs enqueued per max_digests_enqueued_per_30_mins_per_site" do @@ -125,27 +158,29 @@ RSpec.describe Jobs::EnqueueDigestEmails do global_setting :max_digests_enqueued_per_30_mins_per_site, 1 expect_enqueued_with(job: :user_email, args: { type: :digest, user_id: user2.id }) do - expect { Jobs::EnqueueDigestEmails.new.execute(nil) }.to change(Jobs::UserEmail.jobs, :size).by (1) + expect { Jobs::EnqueueDigestEmails.new.execute(nil) }.to change( + Jobs::UserEmail.jobs, + :size, + ).by (1) end # The job didn't actually run, so fake the user_stat update user2.user_stat.update(digest_attempted_at: Time.zone.now) expect_enqueued_with(job: :user_email, args: { type: :digest, user_id: user1.id }) do - expect { Jobs::EnqueueDigestEmails.new.execute(nil) }.to change(Jobs::UserEmail.jobs, :size).by (1) + expect { Jobs::EnqueueDigestEmails.new.execute(nil) }.to change( + Jobs::UserEmail.jobs, + :size, + ).by (1) end user1.user_stat.update(digest_attempted_at: Time.zone.now) - expect_not_enqueued_with(job: :user_email) do - Jobs::EnqueueDigestEmails.new.execute(nil) - end + expect_not_enqueued_with(job: :user_email) { Jobs::EnqueueDigestEmails.new.execute(nil) } end context "when digest emails are enabled" do - before do - Jobs::EnqueueDigestEmails.any_instance.expects(:target_user_ids).returns([user.id]) - end + before { Jobs::EnqueueDigestEmails.any_instance.expects(:target_user_ids).returns([user.id]) } it "enqueues the digest email job" do SiteSetting.disable_digest_emails = false diff --git a/spec/jobs/enqueue_suspect_users_spec.rb b/spec/jobs/enqueue_suspect_users_spec.rb index d1c52285bac..125e2e0de4c 100644 --- a/spec/jobs/enqueue_suspect_users_spec.rb +++ b/spec/jobs/enqueue_suspect_users_spec.rb @@ -3,27 +3,28 @@ RSpec.describe Jobs::EnqueueSuspectUsers do before { SiteSetting.approve_suspect_users = true } - it 'does nothing when there are no suspect users' do + it "does nothing when there are no suspect users" do subject.execute({}) expect(ReviewableUser.count).to be_zero end - context 'with suspect users' do + context "with suspect users" do let!(:suspect_user) { Fabricate(:active_user, created_at: 1.day.ago) } - it 'creates a reviewable when there is a suspect user' do + it "creates a reviewable when there is a suspect user" do subject.execute({}) expect(ReviewableUser.count).to eq(1) end - it 'only creates one reviewable per user' do - review_user = ReviewableUser.needs_review!( - target: suspect_user, - created_by: Discourse.system_user, - reviewable_by_moderator: true - ) + it "only creates one reviewable per user" do + review_user = + ReviewableUser.needs_review!( + target: suspect_user, + created_by: Discourse.system_user, + reviewable_by_moderator: true, + ) subject.execute({}) @@ -31,14 +32,14 @@ RSpec.describe Jobs::EnqueueSuspectUsers do expect(ReviewableUser.last).to eq(review_user) end - it 'adds a score' do + it "adds a score" do subject.execute({}) score = ReviewableScore.last - expect(score.reason).to eq('suspect_user') + expect(score.reason).to eq("suspect_user") end - it 'only enqueues non-approved users' do + it "only enqueues non-approved users" do suspect_user.update!(approved: true) subject.execute({}) @@ -46,7 +47,7 @@ RSpec.describe Jobs::EnqueueSuspectUsers do expect(ReviewableUser.where(target: suspect_user).exists?).to eq(false) end - it 'does nothing if must_approve_users is set to true' do + it "does nothing if must_approve_users is set to true" do SiteSetting.must_approve_users = true suspect_user.update!(approved: false) @@ -55,7 +56,7 @@ RSpec.describe Jobs::EnqueueSuspectUsers do expect(ReviewableUser.where(target: suspect_user).exists?).to eq(false) end - it 'ignores users created more than six months ago' do + it "ignores users created more than six months ago" do suspect_user.update!(created_at: 1.year.ago) subject.execute({}) @@ -63,40 +64,50 @@ RSpec.describe Jobs::EnqueueSuspectUsers do expect(ReviewableUser.where(target: suspect_user).exists?).to eq(false) end - it 'ignores users that were imported from another site' do - suspect_user.upsert_custom_fields({ import_id: 'fake_id' }) + it "ignores users that were imported from another site" do + suspect_user.upsert_custom_fields({ import_id: "fake_id" }) subject.execute({}) expect(ReviewableUser.where(target: suspect_user).exists?).to eq(false) end - it 'enqueues a suspect users with custom fields' do - suspect_user.upsert_custom_fields({ field_a: 'value', field_b: 'value' }) + it "enqueues a suspect users with custom fields" do + suspect_user.upsert_custom_fields({ field_a: "value", field_b: "value" }) subject.execute({}) expect(ReviewableUser.where(target: suspect_user).exists?).to eq(true) end - it 'ignores imported users even if they have multiple custom fields' do - suspect_user.upsert_custom_fields({ field_a: 'value', field_b: 'value', import_id: 'fake_id' }) + it "ignores imported users even if they have multiple custom fields" do + suspect_user.upsert_custom_fields( + { field_a: "value", field_b: "value", import_id: "fake_id" }, + ) subject.execute({}) expect(ReviewableUser.where(target: suspect_user).exists?).to eq(false) end - it 'enqueues a suspect user with not enough time read' do - suspect_user.user_stat.update!(posts_read_count: 2, topics_entered: 2, time_read: 30.seconds.to_i) + it "enqueues a suspect user with not enough time read" do + suspect_user.user_stat.update!( + posts_read_count: 2, + topics_entered: 2, + time_read: 30.seconds.to_i, + ) subject.execute({}) expect(ReviewableUser.count).to eq(1) end - it 'ignores users if their time read is higher than one minute' do - suspect_user.user_stat.update!(posts_read_count: 2, topics_entered: 2, time_read: 2.minutes.to_i) + it "ignores users if their time read is higher than one minute" do + suspect_user.user_stat.update!( + posts_read_count: 2, + topics_entered: 2, + time_read: 2.minutes.to_i, + ) subject.execute({}) diff --git a/spec/jobs/export_csv_file_spec.rb b/spec/jobs/export_csv_file_spec.rb index c126cbc9a85..e395bddec54 100644 --- a/spec/jobs/export_csv_file_spec.rb +++ b/spec/jobs/export_csv_file_spec.rb @@ -1,40 +1,43 @@ # frozen_string_literal: true RSpec.describe Jobs::ExportCsvFile do - - describe '#execute' do + describe "#execute" do let(:other_user) { Fabricate(:user) } let(:admin) { Fabricate(:admin) } let(:action_log) { StaffActionLogger.new(admin).log_revoke_moderation(other_user) } - it 'raises an error when the entity is missing' do - expect { Jobs::ExportCsvFile.new.execute(user_id: admin.id) }.to raise_error(Discourse::InvalidParameters) + it "raises an error when the entity is missing" do + expect { Jobs::ExportCsvFile.new.execute(user_id: admin.id) }.to raise_error( + Discourse::InvalidParameters, + ) end - it 'works' do + it "works" do action_log begin expect do - Jobs::ExportCsvFile.new.execute( - user_id: admin.id, - entity: "staff_action" - ) + Jobs::ExportCsvFile.new.execute(user_id: admin.id, entity: "staff_action") end.to change { Upload.count }.by(1) system_message = admin.topics_allowed.last - expect(system_message.title).to eq(I18n.t( - "system_messages.csv_export_succeeded.subject_template", - export_title: "Staff Action" - )) + expect(system_message.title).to eq( + I18n.t( + "system_messages.csv_export_succeeded.subject_template", + export_title: "Staff Action", + ), + ) upload = system_message.first_post.uploads.first - expect(system_message.first_post.raw).to eq(I18n.t( - "system_messages.csv_export_succeeded.text_body_template", - download_link: "[#{upload.original_filename}|attachment](#{upload.short_url}) (#{upload.filesize} Bytes)" - ).chomp) + expect(system_message.first_post.raw).to eq( + I18n.t( + "system_messages.csv_export_succeeded.text_body_template", + download_link: + "[#{upload.original_filename}|attachment](#{upload.short_url}) (#{upload.filesize} Bytes)", + ).chomp, + ) expect(system_message.id).to eq(UserExport.last.topic_id) expect(system_message.closed).to eq(true) @@ -51,31 +54,35 @@ RSpec.describe Jobs::ExportCsvFile do end end - describe '.report_export' do - + describe ".report_export" do let(:user) { Fabricate(:admin) } let(:exporter) do exporter = Jobs::ExportCsvFile.new - exporter.entity = 'report' - exporter.extra = HashWithIndifferentAccess.new(start_date: '2010-01-01', end_date: '2011-01-01') + exporter.entity = "report" + exporter.extra = + HashWithIndifferentAccess.new(start_date: "2010-01-01", end_date: "2011-01-01") exporter.current_user = User.find_by(id: user.id) exporter end it "does not throw an error when the dates are invalid" do Jobs::ExportCsvFile.new.execute( - entity: 'report', + entity: "report", user_id: user.id, - args: { start_date: 'asdfasdf', end_date: 'not-a-date', name: 'dau_by_mau' } + args: { + start_date: "asdfasdf", + end_date: "not-a-date", + name: "dau_by_mau", + }, ) end - it 'works with single-column reports' do - user.user_visits.create!(visited_at: '2010-01-01', posts_read: 42) - Fabricate(:user).user_visits.create!(visited_at: '2010-01-03', posts_read: 420) + it "works with single-column reports" do + user.user_visits.create!(visited_at: "2010-01-01", posts_read: 42) + Fabricate(:user).user_visits.create!(visited_at: "2010-01-03", posts_read: 420) - exporter.extra['name'] = 'dau_by_mau' + exporter.extra["name"] = "dau_by_mau" report = exporter.report_export.to_a expect(report.first).to contain_exactly("Day", "Percent") @@ -83,16 +90,16 @@ RSpec.describe Jobs::ExportCsvFile do expect(report.third).to contain_exactly("2010-01-03", "50.0") end - it 'works with filters' do - user.user_visits.create!(visited_at: '2010-01-01', posts_read: 42) + it "works with filters" do + user.user_visits.create!(visited_at: "2010-01-01", posts_read: 42) group = Fabricate(:group) user1 = Fabricate(:user) group_user = Fabricate(:group_user, group: group, user: user1) - user1.user_visits.create!(visited_at: '2010-01-03', posts_read: 420) + user1.user_visits.create!(visited_at: "2010-01-03", posts_read: 420) - exporter.extra['name'] = 'visits' - exporter.extra['group'] = group.id + exporter.extra["name"] = "visits" + exporter.extra["group"] = group.id report = exporter.report_export.to_a expect(report.length).to eq(2) @@ -100,11 +107,11 @@ RSpec.describe Jobs::ExportCsvFile do expect(report.second).to contain_exactly("2010-01-03", "1") end - it 'works with single-column reports with default label' do - user.user_visits.create!(visited_at: '2010-01-01') - Fabricate(:user).user_visits.create!(visited_at: '2010-01-03') + it "works with single-column reports with default label" do + user.user_visits.create!(visited_at: "2010-01-01") + Fabricate(:user).user_visits.create!(visited_at: "2010-01-03") - exporter.extra['name'] = 'visits' + exporter.extra["name"] = "visits" report = exporter.report_export.to_a expect(report.first).to contain_exactly("Day", "Count") @@ -112,24 +119,33 @@ RSpec.describe Jobs::ExportCsvFile do expect(report.third).to contain_exactly("2010-01-03", "1") end - it 'works with multi-columns reports' do + it "works with multi-columns reports" do DiscourseIpInfo.stubs(:get).with("1.1.1.1").returns(location: "Earth") - user.user_auth_token_logs.create!(action: "login", client_ip: "1.1.1.1", created_at: '2010-01-01') + user.user_auth_token_logs.create!( + action: "login", + client_ip: "1.1.1.1", + created_at: "2010-01-01", + ) - exporter.extra['name'] = 'staff_logins' + exporter.extra["name"] = "staff_logins" report = exporter.report_export.to_a expect(report.first).to contain_exactly("User", "Location", "Login at") expect(report.second).to contain_exactly(user.username, "Earth", "2010-01-01 00:00:00 UTC") end - it 'works with topic reports' do - freeze_time DateTime.parse('2010-01-01 6:00') + it "works with topic reports" do + freeze_time DateTime.parse("2010-01-01 6:00") - exporter.extra['name'] = 'top_referred_topics' + exporter.extra["name"] = "top_referred_topics" post1 = Fabricate(:post) post2 = Fabricate(:post) - IncomingLink.add(host: "a.com", referer: "http://twitter.com", post_id: post1.id, ip_address: '1.1.1.1') + IncomingLink.add( + host: "a.com", + referer: "http://twitter.com", + post_id: post1.id, + ip_address: "1.1.1.1", + ) report = exporter.report_export.to_a @@ -137,20 +153,20 @@ RSpec.describe Jobs::ExportCsvFile do expect(report.second).to contain_exactly(post1.topic.id.to_s, "1") end - it 'works with stacked_chart reports' do - ApplicationRequest.create!(date: '2010-01-01', req_type: 'page_view_logged_in', count: 1) - ApplicationRequest.create!(date: '2010-01-02', req_type: 'page_view_logged_in', count: 2) - ApplicationRequest.create!(date: '2010-01-03', req_type: 'page_view_logged_in', count: 3) + it "works with stacked_chart reports" do + ApplicationRequest.create!(date: "2010-01-01", req_type: "page_view_logged_in", count: 1) + ApplicationRequest.create!(date: "2010-01-02", req_type: "page_view_logged_in", count: 2) + ApplicationRequest.create!(date: "2010-01-03", req_type: "page_view_logged_in", count: 3) - ApplicationRequest.create!(date: '2010-01-01', req_type: 'page_view_anon', count: 4) - ApplicationRequest.create!(date: '2010-01-02', req_type: 'page_view_anon', count: 5) - ApplicationRequest.create!(date: '2010-01-03', req_type: 'page_view_anon', count: 6) + ApplicationRequest.create!(date: "2010-01-01", req_type: "page_view_anon", count: 4) + ApplicationRequest.create!(date: "2010-01-02", req_type: "page_view_anon", count: 5) + ApplicationRequest.create!(date: "2010-01-03", req_type: "page_view_anon", count: 6) - ApplicationRequest.create!(date: '2010-01-01', req_type: 'page_view_crawler', count: 7) - ApplicationRequest.create!(date: '2010-01-02', req_type: 'page_view_crawler', count: 8) - ApplicationRequest.create!(date: '2010-01-03', req_type: 'page_view_crawler', count: 9) + ApplicationRequest.create!(date: "2010-01-01", req_type: "page_view_crawler", count: 7) + ApplicationRequest.create!(date: "2010-01-02", req_type: "page_view_crawler", count: 8) + ApplicationRequest.create!(date: "2010-01-03", req_type: "page_view_crawler", count: 9) - exporter.extra['name'] = 'consolidated_page_views' + exporter.extra["name"] = "consolidated_page_views" report = exporter.report_export.to_a expect(report[0]).to contain_exactly("Day", "Logged in users", "Anonymous users", "Crawlers") @@ -159,37 +175,74 @@ RSpec.describe Jobs::ExportCsvFile do expect(report[3]).to contain_exactly("2010-01-03", "3", "6", "9") end - it 'works with posts reports and filters' do + it "works with posts reports and filters" do category = Fabricate(:category) subcategory = Fabricate(:category, parent_category: category) - Fabricate(:post, topic: Fabricate(:topic, category: category), created_at: '2010-01-01 12:00:00 UTC') - Fabricate(:post, topic: Fabricate(:topic, category: subcategory), created_at: '2010-01-01 12:00:00 UTC') + Fabricate( + :post, + topic: Fabricate(:topic, category: category), + created_at: "2010-01-01 12:00:00 UTC", + ) + Fabricate( + :post, + topic: Fabricate(:topic, category: subcategory), + created_at: "2010-01-01 12:00:00 UTC", + ) - exporter.extra['name'] = 'posts' + exporter.extra["name"] = "posts" - exporter.extra['category'] = category.id + exporter.extra["category"] = category.id report = exporter.report_export.to_a expect(report[0]).to contain_exactly("Count", "Day") expect(report[1]).to contain_exactly("1", "2010-01-01") - exporter.extra['include_subcategories'] = true + exporter.extra["include_subcategories"] = true report = exporter.report_export.to_a expect(report[0]).to contain_exactly("Count", "Day") expect(report[1]).to contain_exactly("2", "2010-01-01") end end - let(:user_list_header) { - %w{ - id name username email title created_at last_seen_at last_posted_at - last_emailed_at trust_level approved suspended_at suspended_till blocked - active admin moderator ip_address staged secondary_emails topics_entered - posts_read_count time_read topic_count post_count likes_given - likes_received location website views external_id external_email - external_username external_name external_avatar_url - } - } + let(:user_list_header) do + %w[ + id + name + username + email + title + created_at + last_seen_at + last_posted_at + last_emailed_at + trust_level + approved + suspended_at + suspended_till + blocked + active + admin + moderator + ip_address + staged + secondary_emails + topics_entered + posts_read_count + time_read + topic_count + post_count + likes_given + likes_received + location + website + views + external_id + external_email + external_username + external_name + external_avatar_url + ] + end let(:user_list_export) { Jobs::ExportCsvFile.new.user_list_export } @@ -207,12 +260,16 @@ RSpec.describe Jobs::ExportCsvFile do expect(user["secondary_emails"].split(";")).to match_array(secondary_emails) end - it 'exports sso data' do + it "exports sso data" do SiteSetting.discourse_connect_url = "https://www.example.com/sso" SiteSetting.enable_discourse_connect = true user = Fabricate(:user) user.user_profile.update_column(:location, "La,La Land") - user.create_single_sign_on_record(external_id: "123", last_payload: "xxx", external_email: 'test@test.com') + user.create_single_sign_on_record( + external_id: "123", + last_payload: "xxx", + external_email: "test@test.com", + ) user = to_hash(user_list_export.find { |u| u[0].to_i == user.id }) diff --git a/spec/jobs/export_user_archive_spec.rb b/spec/jobs/export_user_archive_spec.rb index 2c047791b26..ea6f91cf1f8 100644 --- a/spec/jobs/export_user_archive_spec.rb +++ b/spec/jobs/export_user_archive_spec.rb @@ -1,18 +1,18 @@ # frozen_string_literal: true -require 'csv' +require "csv" RSpec.describe Jobs::ExportUserArchive do fab!(:user) { Fabricate(:user, username: "john_doe") } fab!(:user2) { Fabricate(:user) } let(:extra) { {} } - let(:job) { + let(:job) do j = Jobs::ExportUserArchive.new j.current_user = user j.extra = extra j - } - let(:component) { raise 'component not set' } + end + let(:component) { raise "component not set" } fab!(:admin) { Fabricate(:admin) } fab!(:category) { Fabricate(:category_with_definition, name: "User Archive Category") } @@ -22,13 +22,17 @@ RSpec.describe Jobs::ExportUserArchive do def make_component_csv data_rows = [] - csv_out = CSV.generate do |csv| - csv << job.get_header(component) - job.public_send(:"#{component}_export") do |row| - csv << row - data_rows << Jobs::ExportUserArchive::HEADER_ATTRS_FOR[component].zip(row.map(&:to_s)).to_h.with_indifferent_access + csv_out = + CSV.generate do |csv| + csv << job.get_header(component) + job.public_send(:"#{component}_export") do |row| + csv << row + data_rows << Jobs::ExportUserArchive::HEADER_ATTRS_FOR[component] + .zip(row.map(&:to_s)) + .to_h + .with_indifferent_access + end end - end [data_rows, csv_out] end @@ -36,16 +40,17 @@ RSpec.describe Jobs::ExportUserArchive do JSON.parse(MultiJson.dump(job.public_send(:"#{component}_export"))) end - describe '#execute' do + describe "#execute" do before do _ = post - user.user_profile.website = 'https://doe.example.com/john' + user.user_profile.website = "https://doe.example.com/john" user.user_profile.save # force a UserAuthTokenLog entry - env = create_request_env.merge( - 'HTTP_USER_AGENT' => 'MyWebBrowser', - 'REQUEST_PATH' => '/some_path/456852', - ) + env = + create_request_env.merge( + "HTTP_USER_AGENT" => "MyWebBrowser", + "REQUEST_PATH" => "/some_path/456852", + ) cookie_jar = ActionDispatch::Request.new(env).cookie_jar Discourse.current_user_provider.new(env).log_on_user(user, {}, cookie_jar) @@ -53,34 +58,37 @@ RSpec.describe Jobs::ExportUserArchive do PostAction.new(user: user, post: post, post_action_type_id: 5).save end - after do - user.uploads.each(&:destroy!) + after { user.uploads.each(&:destroy!) } + + it "raises an error when the user is missing" do + expect { Jobs::ExportCsvFile.new.execute(user_id: user.id + (1 << 20)) }.to raise_error( + Discourse::InvalidParameters, + ) end - it 'raises an error when the user is missing' do - expect { Jobs::ExportCsvFile.new.execute(user_id: user.id + (1 << 20)) }.to raise_error(Discourse::InvalidParameters) - end - - it 'works' do - expect do - Jobs::ExportUserArchive.new.execute( - user_id: user.id, - ) - end.to change { Upload.count }.by(1) + it "works" do + expect do Jobs::ExportUserArchive.new.execute(user_id: user.id) end.to change { + Upload.count + }.by(1) system_message = user.topics_allowed.last - expect(system_message.title).to eq(I18n.t( - "system_messages.csv_export_succeeded.subject_template", - export_title: "User Archive" - )) + expect(system_message.title).to eq( + I18n.t( + "system_messages.csv_export_succeeded.subject_template", + export_title: "User Archive", + ), + ) upload = system_message.first_post.uploads.first - expect(system_message.first_post.raw).to eq(I18n.t( - "system_messages.csv_export_succeeded.text_body_template", - download_link: "[#{upload.original_filename}|attachment](#{upload.short_url}) (#{upload.human_filesize})" - ).chomp) + expect(system_message.first_post.raw).to eq( + I18n.t( + "system_messages.csv_export_succeeded.text_body_template", + download_link: + "[#{upload.original_filename}|attachment](#{upload.short_url}) (#{upload.human_filesize})", + ).chomp, + ) expect(system_message.id).to eq(UserExport.last.topic_id) expect(system_message.closed).to eq(true) @@ -91,37 +99,46 @@ RSpec.describe Jobs::ExportUserArchive do end expect(files.size).to eq(Jobs::ExportUserArchive::COMPONENTS.length) - expect(files.find { |f| f == 'user_archive.csv' }).to_not be_nil - expect(files.find { |f| f == 'category_preferences.csv' }).to_not be_nil + expect(files.find { |f| f == "user_archive.csv" }).to_not be_nil + expect(files.find { |f| f == "category_preferences.csv" }).to_not be_nil end - it 'sends a message if it fails' do + it "sends a message if it fails" do SiteSetting.max_export_file_size_kb = 1 - expect do - Jobs::ExportUserArchive.new.execute( - user_id: user.id, - ) - end.not_to change { Upload.count } + expect do Jobs::ExportUserArchive.new.execute(user_id: user.id) end.not_to change { + Upload.count + } system_message = user.topics_allowed.last - expect(system_message.title).to eq(I18n.t("system_messages.csv_export_failed.subject_template")) + expect(system_message.title).to eq( + I18n.t("system_messages.csv_export_failed.subject_template"), + ) end end - describe 'user_archive posts' do - let(:component) { 'user_archive' } - let(:subsubcategory) { Fabricate(:category_with_definition, parent_category_id: subcategory.id) } + describe "user_archive posts" do + let(:component) { "user_archive" } + let(:subsubcategory) do + Fabricate(:category_with_definition, parent_category_id: subcategory.id) + end let(:subsubtopic) { Fabricate(:topic, category: subsubcategory) } let(:subsubpost) { Fabricate(:post, user: user, topic: subsubtopic) } let(:normal_post) { Fabricate(:post, user: user, topic: topic) } - let(:reply) { PostCreator.new(user2, raw: 'asdf1234qwert7896', topic_id: topic.id, reply_to_post_number: normal_post.post_number).create } + let(:reply) do + PostCreator.new( + user2, + raw: "asdf1234qwert7896", + topic_id: topic.id, + reply_to_post_number: normal_post.post_number, + ).create + end let(:message) { Fabricate(:private_message_topic) } let(:message_post) { Fabricate(:post, user: user, topic: message) } - it 'properly exports posts' do + it "properly exports posts" do SiteSetting.max_category_nesting = 3 [reply, subsubpost, message_post] @@ -129,17 +146,19 @@ RSpec.describe Jobs::ExportUserArchive do rows = [] job.user_archive_export do |row| - rows << Jobs::ExportUserArchive::HEADER_ATTRS_FOR['user_archive'].zip(row).to_h + rows << Jobs::ExportUserArchive::HEADER_ATTRS_FOR["user_archive"].zip(row).to_h end expect(rows.length).to eq(3) - post1 = rows.find { |r| r['topic_title'] == topic.title } - post2 = rows.find { |r| r['topic_title'] == subsubtopic.title } - post3 = rows.find { |r| r['topic_title'] == message.title } + post1 = rows.find { |r| r["topic_title"] == topic.title } + post2 = rows.find { |r| r["topic_title"] == subsubtopic.title } + post3 = rows.find { |r| r["topic_title"] == message.title } expect(post1["categories"]).to eq("#{category.name}") - expect(post2["categories"]).to eq("#{category.name}|#{subcategory.name}|#{subsubcategory.name}") + expect(post2["categories"]).to eq( + "#{category.name}|#{subcategory.name}|#{subsubcategory.name}", + ) expect(post3["categories"]).to eq("-") expect(post1["is_pm"]).to eq(I18n.t("csv_export.boolean_no")) @@ -154,14 +173,14 @@ RSpec.describe Jobs::ExportUserArchive do expect(post2["post_cooked"]).to eq(subsubpost.cooked) expect(post3["post_cooked"]).to eq(message_post.cooked) - expect(post1['like_count']).to eq(1) - expect(post2['like_count']).to eq(0) + expect(post1["like_count"]).to eq(1) + expect(post2["like_count"]).to eq(0) - expect(post1['reply_count']).to eq(1) - expect(post2['reply_count']).to eq(0) + expect(post1["reply_count"]).to eq(1) + expect(post2["reply_count"]).to eq(0) end - it 'can export a post from a deleted category' do + it "can export a post from a deleted category" do cat2 = Fabricate(:category) topic2 = Fabricate(:topic, category: cat2, user: user) _post2 = Fabricate(:post, topic: topic2, user: user) @@ -185,11 +204,11 @@ RSpec.describe Jobs::ExportUserArchive do end end - describe 'preferences' do - let(:component) { 'preferences' } + describe "preferences" do + let(:component) { "preferences" } before do - user.user_profile.website = 'https://doe.example.com/john' + user.user_profile.website = "https://doe.example.com/john" user.user_profile.bio_raw = "I am John Doe\n\nHere I am" user.user_profile.save user.user_option.text_size = :smaller @@ -197,59 +216,60 @@ RSpec.describe Jobs::ExportUserArchive do user.user_option.save end - it 'properly includes the profile fields' do + it "properly includes the profile fields" do _serializer = job.preferences_export # puts MultiJson.dump(serializer, indent: 4) output = make_component_json - payload = output['user'] + payload = output["user"] - expect(payload['website']).to match('doe.example.com') - expect(payload['bio_raw']).to match("Doe\n\nHere") - expect(payload['user_option']['automatically_unpin_topics']).to eq(false) - expect(payload['user_option']['text_size']).to eq('smaller') + expect(payload["website"]).to match("doe.example.com") + expect(payload["bio_raw"]).to match("Doe\n\nHere") + expect(payload["user_option"]["automatically_unpin_topics"]).to eq(false) + expect(payload["user_option"]["text_size"]).to eq("smaller") end end - describe 'auth tokens' do - let(:component) { 'auth_tokens' } + describe "auth tokens" do + let(:component) { "auth_tokens" } before do - env = create_request_env.merge( - 'HTTP_USER_AGENT' => 'MyWebBrowser', - 'REQUEST_PATH' => '/some_path/456852', - ) + env = + create_request_env.merge( + "HTTP_USER_AGENT" => "MyWebBrowser", + "REQUEST_PATH" => "/some_path/456852", + ) cookie_jar = ActionDispatch::Request.new(env).cookie_jar Discourse.current_user_provider.new(env).log_on_user(user, {}, cookie_jar) end - it 'properly includes session records' do + it "properly includes session records" do data, _csv_out = make_component_csv expect(data.length).to eq(1) - expect(data[0]['user_agent']).to eq('MyWebBrowser') + expect(data[0]["user_agent"]).to eq("MyWebBrowser") end - context 'with auth token logs' do - let(:component) { 'auth_token_logs' } - it 'includes details such as the path' do + context "with auth token logs" do + let(:component) { "auth_token_logs" } + it "includes details such as the path" do data, _csv_out = make_component_csv expect(data.length).to eq(1) - expect(data[0]['action']).to eq('generate') - expect(data[0]['path']).to eq('/some_path/456852') + expect(data[0]["action"]).to eq("generate") + expect(data[0]["path"]).to eq("/some_path/456852") end end end - describe 'badges' do - let(:component) { 'badges' } + describe "badges" do + let(:component) { "badges" } let(:badge1) { Fabricate(:badge) } let(:badge2) { Fabricate(:badge, multiple_grant: true) } let(:badge3) { Fabricate(:badge, multiple_grant: true) } let(:day_ago) { 1.day.ago } - it 'properly includes badge records' do + it "properly includes badge records" do grant_start = Time.now.utc BadgeGranter.grant(badge1, user) BadgeGranter.grant(badge2, user) @@ -261,19 +281,19 @@ RSpec.describe Jobs::ExportUserArchive do data, _csv_out = make_component_csv expect(data.length).to eq(6) - expect(data[0]['badge_id']).to eq(badge1.id.to_s) - expect(data[0]['badge_name']).to eq(badge1.display_name) - expect(data[0]['featured_rank']).to_not eq('') - expect(DateTime.parse(data[0]['granted_at'])).to be >= DateTime.parse(grant_start.to_s) - expect(data[2]['granted_manually']).to eq('true') - expect(Post.find(data[3]['post_id'])).to_not be_nil + expect(data[0]["badge_id"]).to eq(badge1.id.to_s) + expect(data[0]["badge_name"]).to eq(badge1.display_name) + expect(data[0]["featured_rank"]).to_not eq("") + expect(DateTime.parse(data[0]["granted_at"])).to be >= DateTime.parse(grant_start.to_s) + expect(data[2]["granted_manually"]).to eq("true") + expect(Post.find(data[3]["post_id"])).to_not be_nil end end - describe 'bookmarks' do - let(:component) { 'bookmarks' } + describe "bookmarks" do + let(:component) { "bookmarks" } - let(:name) { 'Collect my thoughts on this' } + let(:name) { "Collect my thoughts on this" } let(:manager) { BookmarkManager.new(user) } let(:topic1) { Fabricate(:topic) } let(:post1) { Fabricate(:post, topic: topic1, post_number: 5) } @@ -284,15 +304,30 @@ RSpec.describe Jobs::ExportUserArchive do let(:reminder_at) { 1.day.from_now } it "properly includes bookmark records" do - now = freeze_time '2017-03-01 12:00' + now = freeze_time "2017-03-01 12:00" - bookmark1 = manager.create_for(bookmarkable_id: post1.id, bookmarkable_type: "Post", name: name) + bookmark1 = + manager.create_for(bookmarkable_id: post1.id, bookmarkable_type: "Post", name: name) update1_at = now + 1.hours - bookmark1.update(name: 'great food recipe', updated_at: update1_at) + bookmark1.update(name: "great food recipe", updated_at: update1_at) - manager.create_for(bookmarkable_id: post2.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at, options: { auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent] }) + manager.create_for( + bookmarkable_id: post2.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + options: { + auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent], + }, + ) twelve_hr_ago = freeze_time now - 12.hours - pending_reminder = manager.create_for(bookmarkable_id: post3.id, bookmarkable_type: "Post", name: name, reminder_at: now - 8.hours) + pending_reminder = + manager.create_for( + bookmarkable_id: post3.id, + bookmarkable_type: "Post", + name: name, + reminder_at: now - 8.hours, + ) freeze_time now tau_record = private_message_topic.topic_allowed_users.create!(user_id: user.id) @@ -305,35 +340,43 @@ RSpec.describe Jobs::ExportUserArchive do expect(data.length).to eq(4) - expect(data[0]['bookmarkable_id']).to eq(post1.id.to_s) - expect(data[0]['bookmarkable_type']).to eq("Post") - expect(data[0]['link']).to eq(post1.full_url) - expect(DateTime.parse(data[0]['updated_at'])).to eq(DateTime.parse(update1_at.to_s)) + expect(data[0]["bookmarkable_id"]).to eq(post1.id.to_s) + expect(data[0]["bookmarkable_type"]).to eq("Post") + expect(data[0]["link"]).to eq(post1.full_url) + expect(DateTime.parse(data[0]["updated_at"])).to eq(DateTime.parse(update1_at.to_s)) - expect(data[1]['name']).to eq(name) - expect(DateTime.parse(data[1]['reminder_at'])).to eq(DateTime.parse(reminder_at.to_s)) - expect(data[1]['auto_delete_preference']).to eq('when_reminder_sent') + expect(data[1]["name"]).to eq(name) + expect(DateTime.parse(data[1]["reminder_at"])).to eq(DateTime.parse(reminder_at.to_s)) + expect(data[1]["auto_delete_preference"]).to eq("when_reminder_sent") - expect(DateTime.parse(data[2]['created_at'])).to eq(DateTime.parse(twelve_hr_ago.to_s)) - expect(DateTime.parse(data[2]['reminder_last_sent_at'])).to eq(DateTime.parse(now.to_s)) - expect(data[2]['reminder_set_at']).to eq('') + expect(DateTime.parse(data[2]["created_at"])).to eq(DateTime.parse(twelve_hr_ago.to_s)) + expect(DateTime.parse(data[2]["reminder_last_sent_at"])).to eq(DateTime.parse(now.to_s)) + expect(data[2]["reminder_set_at"]).to eq("") - expect(data[3]['bookmarkable_id']).to eq(post4.id.to_s) - expect(data[3]['bookmarkable_type']).to eq("Post") - expect(data[3]['link']).to eq('') + expect(data[3]["bookmarkable_id"]).to eq(post4.id.to_s) + expect(data[3]["bookmarkable_type"]).to eq("Post") + expect(data[3]["link"]).to eq("") end end - describe 'category_preferences' do - let(:component) { 'category_preferences' } + describe "category_preferences" do + let(:component) { "category_preferences" } - let(:subsubcategory) { Fabricate(:category_with_definition, parent_category_id: subcategory.id, name: "User Archive Subcategory") } + let(:subsubcategory) do + Fabricate( + :category_with_definition, + parent_category_id: subcategory.id, + name: "User Archive Subcategory", + ) + end let(:announcements) { Fabricate(:category_with_definition, name: "Announcements") } let(:deleted_category) { Fabricate(:category, name: "Deleted Category") } let(:secure_category_group) { Fabricate(:group) } - let(:secure_category) { Fabricate(:private_category, group: secure_category_group, name: "Super Secret Category") } + let(:secure_category) do + Fabricate(:private_category, group: secure_category_group, name: "Super Secret Category") + end - let(:reset_at) { DateTime.parse('2017-03-01 12:00') } + let(:reset_at) { DateTime.parse("2017-03-01 12:00") } before do SiteSetting.max_category_nesting = 3 @@ -349,61 +392,83 @@ RSpec.describe Jobs::ExportUserArchive do end # Set Watching First Post on announcements, Tracking on subcategory, Muted on deleted, nothing on subsubcategory - CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:watching_first_post], announcements.id) - CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:tracking], subcategory.id) - CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:muted], deleted_category.id) + CategoryUser.set_notification_level_for_category( + user, + NotificationLevels.all[:watching_first_post], + announcements.id, + ) + CategoryUser.set_notification_level_for_category( + user, + NotificationLevels.all[:tracking], + subcategory.id, + ) + CategoryUser.set_notification_level_for_category( + user, + NotificationLevels.all[:muted], + deleted_category.id, + ) deleted_category.destroy! end - it 'correctly exports the CategoryUser table, excluding deleted categories' do + it "correctly exports the CategoryUser table, excluding deleted categories" do data, _csv_out = make_component_csv - expect(data.find { |r| r['category_id'] == category.id.to_s }).to be_nil - expect(data.find { |r| r['category_id'] == deleted_category.id.to_s }).to be_nil + expect(data.find { |r| r["category_id"] == category.id.to_s }).to be_nil + expect(data.find { |r| r["category_id"] == deleted_category.id.to_s }).to be_nil expect(data.length).to eq(3) - data.sort! { |a, b| a['category_id'].to_i <=> b['category_id'].to_i } + data.sort! { |a, b| a["category_id"].to_i <=> b["category_id"].to_i } expect(data[0][:category_id]).to eq(subcategory.id.to_s) - expect(data[0][:notification_level].to_s).to eq('tracking') + expect(data[0][:notification_level].to_s).to eq("tracking") expect(DateTime.parse(data[0][:dismiss_new_timestamp])).to eq(reset_at) expect(data[1][:category_id]).to eq(subsubcategory.id.to_s) - expect(data[1][:category_names]).to eq("#{category.name}|#{subcategory.name}|#{subsubcategory.name}") - expect(data[1][:notification_level]).to eq('regular') + expect(data[1][:category_names]).to eq( + "#{category.name}|#{subcategory.name}|#{subsubcategory.name}", + ) + expect(data[1][:notification_level]).to eq("regular") expect(DateTime.parse(data[1][:dismiss_new_timestamp])).to eq(reset_at) expect(data[2][:category_id]).to eq(announcements.id.to_s) expect(data[2][:category_names]).to eq(announcements.name) - expect(data[2][:notification_level]).to eq('watching_first_post') - expect(data[2][:dismiss_new_timestamp]).to eq('') + expect(data[2][:notification_level]).to eq("watching_first_post") + expect(data[2][:dismiss_new_timestamp]).to eq("") end it "does not include any secure categories the user does not have access to, even if the user has a CategoryUser record" do - CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:muted], secure_category.id) + CategoryUser.set_notification_level_for_category( + user, + NotificationLevels.all[:muted], + secure_category.id, + ) data, _csv_out = make_component_csv - expect(data.any? { |r| r['category_id'] == secure_category.id.to_s }).to eq(false) + expect(data.any? { |r| r["category_id"] == secure_category.id.to_s }).to eq(false) expect(data.length).to eq(3) end it "does include secure categories that the user has access to" do - CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:muted], secure_category.id) + CategoryUser.set_notification_level_for_category( + user, + NotificationLevels.all[:muted], + secure_category.id, + ) GroupUser.create!(user: user, group: secure_category_group) data, _csv_out = make_component_csv - expect(data.any? { |r| r['category_id'] == secure_category.id.to_s }).to eq(true) + expect(data.any? { |r| r["category_id"] == secure_category.id.to_s }).to eq(true) expect(data.length).to eq(4) end end - describe 'flags' do - let(:component) { 'flags' } + describe "flags" do + let(:component) { "flags" } let(:other_post) { Fabricate(:post, user: admin) } let(:post3) { Fabricate(:post) } let(:post4) { Fabricate(:post) } - it 'correctly exports flags' do + it "correctly exports flags" do result0 = PostActionCreator.notify_moderators(user, other_post, "helping out the admins") PostActionCreator.spam(user, post3) PostActionDestroyer.destroy(user, post3, :spam) @@ -414,30 +479,30 @@ RSpec.describe Jobs::ExportUserArchive do data, _csv_out = make_component_csv expect(data.length).to eq(4) - data.sort_by! { |row| row['post_id'].to_i } + data.sort_by! { |row| row["post_id"].to_i } - expect(data[0]['post_id']).to eq(other_post.id.to_s) - expect(data[0]['flag_type']).to eq('notify_moderators') - expect(data[0]['related_post_id']).to eq(result0.post_action.related_post_id.to_s) + expect(data[0]["post_id"]).to eq(other_post.id.to_s) + expect(data[0]["flag_type"]).to eq("notify_moderators") + expect(data[0]["related_post_id"]).to eq(result0.post_action.related_post_id.to_s) - expect(data[1]['flag_type']).to eq('spam') - expect(data[2]['flag_type']).to eq('inappropriate') - expect(data[1]['deleted_at']).to_not be_empty - expect(data[1]['deleted_by']).to eq('self') - expect(data[2]['deleted_at']).to be_empty + expect(data[1]["flag_type"]).to eq("spam") + expect(data[2]["flag_type"]).to eq("inappropriate") + expect(data[1]["deleted_at"]).to_not be_empty + expect(data[1]["deleted_by"]).to eq("self") + expect(data[2]["deleted_at"]).to be_empty - expect(data[3]['post_id']).to eq(post4.id.to_s) - expect(data[3]['flag_type']).to eq('off_topic') - expect(data[3]['deleted_at']).to be_empty + expect(data[3]["post_id"]).to eq(post4.id.to_s) + expect(data[3]["flag_type"]).to eq("off_topic") + expect(data[3]["deleted_at"]).to be_empty end end - describe 'likes' do - let(:component) { 'likes' } + describe "likes" do + let(:component) { "likes" } let(:other_post) { Fabricate(:post, user: admin) } let(:post3) { Fabricate(:post) } - it 'correctly exports likes' do + it "correctly exports likes" do PostActionCreator.like(user, other_post) PostActionCreator.like(user, post3) PostActionCreator.like(admin, post3) @@ -446,25 +511,27 @@ RSpec.describe Jobs::ExportUserArchive do data, _csv_out = make_component_csv expect(data.length).to eq(2) - data.sort_by! { |row| row['post_id'].to_i } + data.sort_by! { |row| row["post_id"].to_i } - expect(data[0]['post_id']).to eq(other_post.id.to_s) - expect(data[1]['post_id']).to eq(post3.id.to_s) - expect(data[1]['deleted_at']).to_not be_empty - expect(data[1]['deleted_by']).to eq('self') + expect(data[0]["post_id"]).to eq(other_post.id.to_s) + expect(data[1]["post_id"]).to eq(post3.id.to_s) + expect(data[1]["deleted_at"]).to_not be_empty + expect(data[1]["deleted_by"]).to eq("self") end end - describe 'queued posts' do - let(:component) { 'queued_posts' } + describe "queued posts" do + let(:component) { "queued_posts" } let(:reviewable_post) { Fabricate(:reviewable_queued_post, topic: topic, created_by: user) } - let(:reviewable_topic) { Fabricate(:reviewable_queued_post_topic, category: category, created_by: user) } + let(:reviewable_topic) do + Fabricate(:reviewable_queued_post_topic, category: category, created_by: user) + end - it 'correctly exports queued posts' do + it "correctly exports queued posts" do SiteSetting.tagging_enabled = true reviewable_post.perform(admin, :reject_post) - reviewable_topic.payload['tags'] = ['example_tag'] + reviewable_topic.payload["tags"] = ["example_tag"] result = reviewable_topic.perform(admin, :approve_post) expect(result.success?).to eq(true) @@ -475,35 +542,65 @@ RSpec.describe Jobs::ExportUserArchive do approved = data.find { |el| el["verdict"] === "approved" } rejected = data.find { |el| el["verdict"] === "rejected" } - expect(approved['other_json']).to match('example_tag') - expect(rejected['post_raw']).to eq('hello world post contents.') - expect(rejected['other_json']).to match('reply_to_post_number') + expect(approved["other_json"]).to match("example_tag") + expect(rejected["post_raw"]).to eq("hello world post contents.") + expect(rejected["other_json"]).to match("reply_to_post_number") end end - describe 'visits' do - let(:component) { 'visits' } + describe "visits" do + let(:component) { "visits" } - it 'correctly exports the UserVisit table' do - freeze_time '2017-03-01 12:00' + it "correctly exports the UserVisit table" do + freeze_time "2017-03-01 12:00" - UserVisit.create(user_id: user.id, visited_at: 1.minute.ago, posts_read: 1, mobile: false, time_read: 10) - UserVisit.create(user_id: user.id, visited_at: 2.days.ago, posts_read: 2, mobile: false, time_read: 20) - UserVisit.create(user_id: user.id, visited_at: 1.week.ago, posts_read: 3, mobile: true, time_read: 30) - UserVisit.create(user_id: user.id, visited_at: 1.year.ago, posts_read: 4, mobile: false, time_read: 40) - UserVisit.create(user_id: user2.id, visited_at: 1.minute.ago, posts_read: 1, mobile: false, time_read: 50) + UserVisit.create( + user_id: user.id, + visited_at: 1.minute.ago, + posts_read: 1, + mobile: false, + time_read: 10, + ) + UserVisit.create( + user_id: user.id, + visited_at: 2.days.ago, + posts_read: 2, + mobile: false, + time_read: 20, + ) + UserVisit.create( + user_id: user.id, + visited_at: 1.week.ago, + posts_read: 3, + mobile: true, + time_read: 30, + ) + UserVisit.create( + user_id: user.id, + visited_at: 1.year.ago, + posts_read: 4, + mobile: false, + time_read: 40, + ) + UserVisit.create( + user_id: user2.id, + visited_at: 1.minute.ago, + posts_read: 1, + mobile: false, + time_read: 50, + ) data, _csv_out = make_component_csv # user2's data is not mixed in expect(data.length).to eq(4) - expect(data.find { |r| r['time_read'] == 50 }).to be_nil + expect(data.find { |r| r["time_read"] == 50 }).to be_nil - expect(data[0]['visited_at']).to eq('2016-03-01') - expect(data[0]['posts_read']).to eq('4') - expect(data[0]['time_read']).to eq('40') - expect(data[1]['mobile']).to eq('true') - expect(data[3]['visited_at']).to eq('2017-03-01') + expect(data[0]["visited_at"]).to eq("2016-03-01") + expect(data[0]["posts_read"]).to eq("4") + expect(data[0]["time_read"]).to eq("40") + expect(data[1]["mobile"]).to eq("true") + expect(data[3]["visited_at"]).to eq("2017-03-01") end end end diff --git a/spec/jobs/feature_topic_users_spec.rb b/spec/jobs/feature_topic_users_spec.rb index 3fa7eba0928..4eecff5e541 100644 --- a/spec/jobs/feature_topic_users_spec.rb +++ b/spec/jobs/feature_topic_users_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Jobs::FeatureTopicUsers do Jobs::FeatureTopicUsers.new.execute(topic_id: 123) end - context 'with a topic' do + context "with a topic" do let!(:post) { create_post } let(:topic) { post.topic } fab!(:coding_horror) { Fabricate(:coding_horror) } diff --git a/spec/jobs/fix_out_of_sync_user_uploaded_avatar_spec.rb b/spec/jobs/fix_out_of_sync_user_uploaded_avatar_spec.rb index b6da24ac573..bd5a32a7458 100644 --- a/spec/jobs/fix_out_of_sync_user_uploaded_avatar_spec.rb +++ b/spec/jobs/fix_out_of_sync_user_uploaded_avatar_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Jobs::FixOutOfSyncUserUploadedAvatar do - it 'should fix out of sync user uploaded avatars' do + it "should fix out of sync user uploaded avatars" do user_with_custom_upload = Fabricate(:user) custom_upload1 = Fabricate(:upload, user: user_with_custom_upload) gravatar_upload1 = Fabricate(:upload, user: user_with_custom_upload) @@ -9,7 +9,7 @@ RSpec.describe Jobs::FixOutOfSyncUserUploadedAvatar do user_with_custom_upload.user_avatar.update!( custom_upload: custom_upload1, - gravatar_upload: gravatar_upload1 + gravatar_upload: gravatar_upload1, ) user_out_of_sync = Fabricate(:user) @@ -22,23 +22,19 @@ RSpec.describe Jobs::FixOutOfSyncUserUploadedAvatar do user_out_of_sync.user_avatar.update!( custom_upload: custom_upload2, - gravatar_upload: gravatar_upload2 + gravatar_upload: gravatar_upload2, ) user_without_uploaded_avatar = Fabricate(:user) gravatar_upload3 = Fabricate(:upload, user: user_without_uploaded_avatar) - user_without_uploaded_avatar.user_avatar.update!( - gravatar_upload: gravatar_upload3 - ) + user_without_uploaded_avatar.user_avatar.update!(gravatar_upload: gravatar_upload3) described_class.new.execute_onceoff({}) expect(user_with_custom_upload.reload.uploaded_avatar).to eq(custom_upload1) expect(user_out_of_sync.reload.uploaded_avatar).to eq(gravatar_upload2) - expect(user_without_uploaded_avatar.reload.uploaded_avatar) - .to eq(nil) - + expect(user_without_uploaded_avatar.reload.uploaded_avatar).to eq(nil) end end diff --git a/spec/jobs/fix_primary_emails_for_staged_users_spec.rb b/spec/jobs/fix_primary_emails_for_staged_users_spec.rb index 54a997c4778..fbf4af96241 100644 --- a/spec/jobs/fix_primary_emails_for_staged_users_spec.rb +++ b/spec/jobs/fix_primary_emails_for_staged_users_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true RSpec.describe Jobs::FixPrimaryEmailsForStagedUsers do - it 'should clean up duplicated staged users' do - common_email = 'test@reply' + it "should clean up duplicated staged users" do + common_email = "test@reply" staged_user = Fabricate(:user, staged: true, active: false) staged_user2 = Fabricate(:user, staged: true, active: false) @@ -24,11 +24,15 @@ RSpec.describe Jobs::FixPrimaryEmailsForStagedUsers do # it will raise error in https://github.com/discourse/discourse/blob/d0b027d88deeabf8bc105419f7d3fae0087091cd/app/models/user.rb#L942 WebHook.stubs(:generate_payload).returns(nil) - expect { described_class.new.execute_onceoff({}) } - .to change { User.count }.by(-2) - .and change { staged_user.posts.count }.by(3) + expect { described_class.new.execute_onceoff({}) }.to change { User.count }.by(-2).and change { + staged_user.posts.count + }.by(3) - expect(User.where('id > -2')).to contain_exactly(Discourse.system_user, staged_user, active_user) + expect(User.where("id > -2")).to contain_exactly( + Discourse.system_user, + staged_user, + active_user, + ) expect(staged_user.posts.all).to contain_exactly(post1, post2, post3) expect(staged_user.reload.email).to eq(common_email) end diff --git a/spec/jobs/fix_s3_etags_spec.rb b/spec/jobs/fix_s3_etags_spec.rb index 225837fed15..2f72829f089 100644 --- a/spec/jobs/fix_s3_etags_spec.rb +++ b/spec/jobs/fix_s3_etags_spec.rb @@ -2,9 +2,9 @@ RSpec.describe Jobs::FixS3Etags do let(:etag_with_quotes) { '"ETag"' } - let(:etag_without_quotes) { 'ETag' } + let(:etag_without_quotes) { "ETag" } - it 'should remove double quotes from etags' do + it "should remove double quotes from etags" do upload1 = Fabricate(:upload, etag: etag_with_quotes) upload2 = Fabricate(:upload, etag: etag_without_quotes) optimized = Fabricate(:optimized_image, etag: etag_with_quotes) diff --git a/spec/jobs/fix_user_usernames_and_groups_names_clash_spec.rb b/spec/jobs/fix_user_usernames_and_groups_names_clash_spec.rb index e237f9c6bee..05ba09781d0 100644 --- a/spec/jobs/fix_user_usernames_and_groups_names_clash_spec.rb +++ b/spec/jobs/fix_user_usernames_and_groups_names_clash_spec.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true RSpec.describe Jobs::FixUserUsernamesAndGroupsNamesClash do - it 'update usernames of users that clashes with a group name' do + it "update usernames of users that clashes with a group name" do user = Fabricate(:user) - Fabricate(:user, username: 'test1') - group = Fabricate(:group, name: 'test') - user.update_columns(username: 'test', username_lower: 'test') + Fabricate(:user, username: "test1") + group = Fabricate(:group, name: "test") + user.update_columns(username: "test", username_lower: "test") Jobs::FixUserUsernamesAndGroupsNamesClash.new.execute({}) - expect(user.reload.username).to eq('test2') - expect(group.reload.name).to eq('test') + expect(user.reload.username).to eq("test2") + expect(group.reload.name).to eq("test") end end diff --git a/spec/jobs/grant_anniversary_badges_spec.rb b/spec/jobs/grant_anniversary_badges_spec.rb index ae5cad056c2..5cee3130870 100644 --- a/spec/jobs/grant_anniversary_badges_spec.rb +++ b/spec/jobs/grant_anniversary_badges_spec.rb @@ -100,9 +100,7 @@ RSpec.describe Jobs::GrantAnniversaryBadges do user = Fabricate(:user, created_at: 800.days.ago) Fabricate(:post, user: user, created_at: 450.days.ago) - freeze_time(400.days.ago) do - granter.execute({}) - end + freeze_time(400.days.ago) { granter.execute({}) } badge = user.user_badges.where(badge_id: Badge::Anniversary) expect(badge.count).to eq(1) diff --git a/spec/jobs/grant_new_user_of_the_month_badges_spec.rb b/spec/jobs/grant_new_user_of_the_month_badges_spec.rb index 419f709cad0..9ac859a5143 100644 --- a/spec/jobs/grant_new_user_of_the_month_badges_spec.rb +++ b/spec/jobs/grant_new_user_of_the_month_badges_spec.rb @@ -1,16 +1,15 @@ # frozen_string_literal: true RSpec.describe Jobs::GrantNewUserOfTheMonthBadges do - let(:granter) { described_class.new } it "runs correctly" do - freeze_time(DateTime.parse('2019-11-30 23:59 UTC')) + freeze_time(DateTime.parse("2019-11-30 23:59 UTC")) u0 = Fabricate(:user, created_at: 2.weeks.ago) BadgeGranter.grant(Badge.find(Badge::NewUserOfTheMonth), u0, created_at: Time.now) - freeze_time(DateTime.parse('2020-01-01 00:00 UTC')) + freeze_time(DateTime.parse("2020-01-01 00:00 UTC")) user = Fabricate(:user, created_at: 1.week.ago) p = Fabricate(:post, user: user) @@ -25,11 +24,11 @@ RSpec.describe Jobs::GrantNewUserOfTheMonthBadges do badges = user.user_badges.where(badge_id: Badge::NewUserOfTheMonth) expect(badges).to be_present - expect(badges.first.granted_at.to_s).to eq('2019-12-31 23:59:59 UTC') + expect(badges.first.granted_at.to_s).to eq("2019-12-31 23:59:59 UTC") end it "does not include people created after the previous month" do - freeze_time(DateTime.parse('2020-01-15 00:00 UTC')) + freeze_time(DateTime.parse("2020-01-15 00:00 UTC")) user = Fabricate(:user, created_at: 1.week.ago) p = Fabricate(:post, user: user) @@ -85,12 +84,12 @@ RSpec.describe Jobs::GrantNewUserOfTheMonthBadges do end it "does nothing if it's already been awarded in previous month" do - freeze_time(DateTime.parse('2019-11-30 23:59 UTC')) + freeze_time(DateTime.parse("2019-11-30 23:59 UTC")) u0 = Fabricate(:user, created_at: 2.weeks.ago) BadgeGranter.grant(Badge.find(Badge::NewUserOfTheMonth), u0, created_at: Time.now) - freeze_time(DateTime.parse('2019-12-01 00:00 UTC')) + freeze_time(DateTime.parse("2019-12-01 00:00 UTC")) user = Fabricate(:user, created_at: 1.week.ago) p = Fabricate(:post, user: user) @@ -107,7 +106,7 @@ RSpec.describe Jobs::GrantNewUserOfTheMonthBadges do expect(badge).to be_blank end - describe '.scores' do + describe ".scores" do def scores granter.scores(1.month.ago, Time.now) end @@ -223,7 +222,5 @@ RSpec.describe Jobs::GrantNewUserOfTheMonthBadges do expect(scores.keys.size).to eq(2) end - end - end diff --git a/spec/jobs/heartbeat_spec.rb b/spec/jobs/heartbeat_spec.rb index f62043e2871..983b8832c54 100644 --- a/spec/jobs/heartbeat_spec.rb +++ b/spec/jobs/heartbeat_spec.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true RSpec.describe ::Jobs::Heartbeat do - after do - Discourse.disable_readonly_mode - end + after { Discourse.disable_readonly_mode } it "still enqueues heartbeats in readonly mode" do freeze_time 1.week.from_now diff --git a/spec/jobs/ignored_users_summary_spec.rb b/spec/jobs/ignored_users_summary_spec.rb index 1768161a7c8..461d94081a2 100644 --- a/spec/jobs/ignored_users_summary_spec.rb +++ b/spec/jobs/ignored_users_summary_spec.rb @@ -27,9 +27,7 @@ RSpec.describe Jobs::IgnoredUsersSummary do context "when no system message exists for the ignored users" do context "when threshold is not hit" do - before do - SiteSetting.ignored_users_count_message_threshold = 5 - end + before { SiteSetting.ignored_users_count_message_threshold = 5 } it "does nothing" do subject @@ -40,10 +38,13 @@ RSpec.describe Jobs::IgnoredUsersSummary do context "when threshold is hit" do it "creates a system message" do subject - posts = Post.joins(:topic).where(topics: { - archetype: Archetype.private_message, - subtype: TopicSubtype.system_message - }) + posts = + Post.joins(:topic).where( + topics: { + archetype: Archetype.private_message, + subtype: TopicSubtype.system_message, + }, + ) expect(posts.count).to eq(2) expect(posts.find { |post| post.raw.include?(matt.username) }).to be_present expect(posts.find { |post| post.raw.include?(john.username) }).to be_present @@ -53,9 +54,7 @@ RSpec.describe Jobs::IgnoredUsersSummary do context "when a system message already exists for the ignored users" do context "when threshold is not hit" do - before do - SiteSetting.ignored_users_count_message_threshold = 5 - end + before { SiteSetting.ignored_users_count_message_threshold = 5 } it "does nothing" do subject diff --git a/spec/jobs/invalidate_inactive_admins_spec.rb b/spec/jobs/invalidate_inactive_admins_spec.rb index 8994e9f7304..7c070aa5c1d 100644 --- a/spec/jobs/invalidate_inactive_admins_spec.rb +++ b/spec/jobs/invalidate_inactive_admins_spec.rb @@ -17,17 +17,15 @@ RSpec.describe Jobs::InvalidateInactiveAdmins do fab!(:not_seen_admin) { Fabricate(:admin, last_seen_at: 370.days.ago) } before { not_seen_admin.email_tokens.update_all(confirmed: true) } - context 'when invalidate_inactive_admin_email_after_days = 365' do - before do - SiteSetting.invalidate_inactive_admin_email_after_days = 365 - end + context "when invalidate_inactive_admin_email_after_days = 365" do + before { SiteSetting.invalidate_inactive_admin_email_after_days = 365 } - it 'marks email tokens as unconfirmed' do + it "marks email tokens as unconfirmed" do subject expect(not_seen_admin.reload.email_tokens.where(confirmed: true).exists?).to eq(false) end - it 'makes the user as not active and logs the action' do + it "makes the user as not active and logs the action" do subject expect(not_seen_admin.reload.active).to eq(false) @@ -36,17 +34,24 @@ RSpec.describe Jobs::InvalidateInactiveAdmins do expect(log.action).to eq(UserHistory.actions[:deactivate_user]) end - it 'adds a staff log' do + it "adds a staff log" do subject expect(not_seen_admin.reload.active).to eq(false) end - context 'with social logins' do + context "with social logins" do before do - UserAssociatedAccount.create!(provider_name: "google_oauth2", user_id: not_seen_admin.id, provider_uid: 100, info: { email: "bob@google.account.com" }) + UserAssociatedAccount.create!( + provider_name: "google_oauth2", + user_id: not_seen_admin.id, + provider_uid: 100, + info: { + email: "bob@google.account.com", + }, + ) end - it 'removes the social logins' do + it "removes the social logins" do subject expect(UserAssociatedAccount.where(user_id: not_seen_admin.id).exists?).to eq(false) end @@ -65,12 +70,10 @@ RSpec.describe Jobs::InvalidateInactiveAdmins do end end - context 'when invalidate_inactive_admin_email_after_days = 0 to disable this feature' do - before do - SiteSetting.invalidate_inactive_admin_email_after_days = 0 - end + context "when invalidate_inactive_admin_email_after_days = 0 to disable this feature" do + before { SiteSetting.invalidate_inactive_admin_email_after_days = 0 } - it 'does nothing' do + it "does nothing" do subject expect(active_admin.reload.active).to eq(true) expect(active_admin.email_tokens.where(confirmed: true).exists?).to eq(true) diff --git a/spec/jobs/invite_email_spec.rb b/spec/jobs/invite_email_spec.rb index ec4bae6c519..0d6b52d4838 100644 --- a/spec/jobs/invite_email_spec.rb +++ b/spec/jobs/invite_email_spec.rb @@ -1,19 +1,18 @@ # frozen_string_literal: true RSpec.describe Jobs::InviteEmail do - - describe '.execute' do - - it 'raises an error when the invite_id is missing' do + describe ".execute" do + it "raises an error when the invite_id is missing" do expect { Jobs::InviteEmail.new.execute({}) }.to raise_error(Discourse::InvalidParameters) end - context 'with an invite id' do - - let (:mailer) { Mail::Message.new(to: 'eviltrout@test.domain') } + context "with an invite id" do + let (:mailer) { + Mail::Message.new(to: "eviltrout@test.domain") + } fab!(:invite) { Fabricate(:invite) } - it 'delegates to the test mailer' do + it "delegates to the test mailer" do Email::Sender.any_instance.expects(:send) InviteMailer.expects(:send_invite).with(invite, anything).returns(mailer) Jobs::InviteEmail.new.execute(invite_id: invite.id) diff --git a/spec/jobs/jobs_base_spec.rb b/spec/jobs/jobs_base_spec.rb index 6b267af9c3c..19899879bfa 100644 --- a/spec/jobs/jobs_base_spec.rb +++ b/spec/jobs/jobs_base_spec.rb @@ -22,14 +22,14 @@ RSpec.describe ::Jobs::Base do end end - it 'handles correct jobs' do + it "handles correct jobs" do job = GoodJob.new job.perform({}) expect(job.count).to eq(1) end - it 'handles errors in multisite' do - RailsMultisite::ConnectionManagement.expects(:all_dbs).returns(['default', 'default', 'default']) + it "handles errors in multisite" do + RailsMultisite::ConnectionManagement.expects(:all_dbs).returns(%w[default default default]) # one exception per database Discourse.expects(:handle_job_exception).times(3) @@ -38,17 +38,13 @@ RSpec.describe ::Jobs::Base do expect(bad.fail_count).to eq(3) end - describe '#perform' do - context 'when a job raises an error' do - before do - Discourse.reset_job_exception_stats! - end + describe "#perform" do + context "when a job raises an error" do + before { Discourse.reset_job_exception_stats! } - after do - Discourse.reset_job_exception_stats! - end + after { Discourse.reset_job_exception_stats! } - it 'collects stats for failing jobs in Discourse.job_exception_stats' do + it "collects stats for failing jobs in Discourse.job_exception_stats" do bad = BadJob.new 3.times do # During test env handle_job_exception errors out @@ -61,51 +57,47 @@ RSpec.describe ::Jobs::Base do end end - it 'delegates the process call to execute' do - ::Jobs::Base.any_instance.expects(:execute).with({ 'hello' => 'world' }) - ::Jobs::Base.new.perform('hello' => 'world', 'sync_exec' => true) + it "delegates the process call to execute" do + ::Jobs::Base.any_instance.expects(:execute).with({ "hello" => "world" }) + ::Jobs::Base.new.perform("hello" => "world", "sync_exec" => true) end - it 'converts to an indifferent access hash' do + it "converts to an indifferent access hash" do ::Jobs::Base.any_instance.expects(:execute).with(instance_of(HashWithIndifferentAccess)) - ::Jobs::Base.new.perform('hello' => 'world', 'sync_exec' => true) + ::Jobs::Base.new.perform("hello" => "world", "sync_exec" => true) end context "with fake jobs" do let(:common_state) { [] } - let(:test_job_1) { - Class.new(Jobs::Base).tap do |klass| - state = common_state - klass.define_method(:execute) do |args| - state << "job_1_executed" + let(:test_job_1) do + Class + .new(Jobs::Base) + .tap do |klass| + state = common_state + klass.define_method(:execute) { |args| state << "job_1_executed" } end - end - } + end - let(:test_job_2) { - Class.new(Jobs::Base).tap do |klass| - state = common_state - job_1 = test_job_1 - klass.define_method(:execute) do |args| - state << "job_2_started" - Jobs.enqueue(job_1) - state << "job_2_finished" + let(:test_job_2) do + Class + .new(Jobs::Base) + .tap do |klass| + state = common_state + job_1 = test_job_1 + klass.define_method(:execute) do |args| + state << "job_2_started" + Jobs.enqueue(job_1) + state << "job_2_finished" + end end - end - } + end it "runs jobs synchronously sequentially in tests" do Jobs.run_immediately! Jobs.enqueue(test_job_2) - expect(common_state).to eq([ - "job_2_started", - "job_2_finished", - "job_1_executed" - ]) + expect(common_state).to eq(%w[job_2_started job_2_finished job_1_executed]) end - end - end diff --git a/spec/jobs/jobs_spec.rb b/spec/jobs/jobs_spec.rb index 09406eb66be..c20f23a89dd 100644 --- a/spec/jobs/jobs_spec.rb +++ b/spec/jobs/jobs_spec.rb @@ -1,15 +1,11 @@ # frozen_string_literal: true RSpec.describe Jobs do + describe "enqueue" do + describe "run_later!" do + before { Jobs.run_later! } - describe 'enqueue' do - - describe 'run_later!' do - before do - Jobs.run_later! - end - - it 'enqueues a job in sidekiq' do + it "enqueues a job in sidekiq" do Sidekiq::Testing.fake! do jobs = Jobs::ProcessPost.jobs @@ -21,7 +17,7 @@ RSpec.describe Jobs do expected = { "class" => "Jobs::ProcessPost", "args" => [{ "post_id" => 1, "current_site_id" => "default" }], - "queue" => "default" + "queue" => "default", } expect(job.slice("class", "args", "queue")).to eq(expected) end @@ -62,7 +58,7 @@ RSpec.describe Jobs do expected = { "class" => "Jobs::ProcessPost", "args" => [{ "post_id" => 1 }], - "queue" => "default" + "queue" => "default", } expect(job.slice("class", "args", "queue")).to eq(expected) end @@ -75,12 +71,11 @@ RSpec.describe Jobs do end it "should enqueue with the correct database id when the current_site_id option is given" do - Sidekiq::Testing.fake! do jobs = Jobs::ProcessPost.jobs jobs.clear - Jobs.enqueue(:process_post, post_id: 1, current_site_id: 'test_db') + Jobs.enqueue(:process_post, post_id: 1, current_site_id: "test_db") expect(jobs.length).to eq(1) job = jobs.first @@ -88,17 +83,15 @@ RSpec.describe Jobs do expected = { "class" => "Jobs::ProcessPost", "args" => [{ "post_id" => 1, "current_site_id" => "test_db" }], - "queue" => "default" + "queue" => "default", } expect(job.slice("class", "args", "queue")).to eq(expected) end end end - describe 'run_immediately!' do - before do - Jobs.run_immediately! - end + describe "run_immediately!" do + before { Jobs.run_immediately! } it "doesn't enqueue in sidekiq" do Sidekiq::Client.expects(:enqueue).with(Jobs::ProcessPost, {}).never @@ -106,45 +99,45 @@ RSpec.describe Jobs do end it "executes the job right away" do - Jobs::ProcessPost.any_instance.expects(:perform).with({ "post_id" => 1, "sync_exec" => true, "current_site_id" => "default" }) + Jobs::ProcessPost + .any_instance + .expects(:perform) + .with({ "post_id" => 1, "sync_exec" => true, "current_site_id" => "default" }) Jobs.enqueue(:process_post, post_id: 1) end - context 'when current_site_id option is given and does not match the current connection' do + context "when current_site_id option is given and does not match the current connection" do before do Sidekiq::Client.stubs(:enqueue) Jobs::ProcessPost.any_instance.stubs(:execute).returns(true) end - it 'should raise an exception' do + it "should raise an exception" do Jobs::ProcessPost.any_instance.expects(:execute).never RailsMultisite::ConnectionManagement.expects(:establish_connection).never expect { - Jobs.enqueue(:process_post, post_id: 1, current_site_id: 'test_db') + Jobs.enqueue(:process_post, post_id: 1, current_site_id: "test_db") }.to raise_error(ArgumentError) end end end - end - describe 'cancel_scheduled_job' do + describe "cancel_scheduled_job" do let(:scheduled_jobs) { Sidekiq::ScheduledSet.new } - after do - scheduled_jobs.clear - end + after { scheduled_jobs.clear } - it 'deletes the matching job' do + it "deletes the matching job" do Sidekiq::Testing.disable! do scheduled_jobs.clear expect(scheduled_jobs.size).to eq(0) Jobs.enqueue_in(1.year, :run_heartbeat, topic_id: 123) Jobs.enqueue_in(2.years, :run_heartbeat, topic_id: 456) - Jobs.enqueue_in(3.years, :run_heartbeat, topic_id: 123, current_site_id: 'foo') - Jobs.enqueue_in(4.years, :run_heartbeat, topic_id: 123, current_site_id: 'bar') + Jobs.enqueue_in(3.years, :run_heartbeat, topic_id: 123, current_site_id: "foo") + Jobs.enqueue_in(4.years, :run_heartbeat, topic_id: 123, current_site_id: "bar") expect(scheduled_jobs.size).to eq(4) @@ -157,11 +150,10 @@ RSpec.describe Jobs do expect(scheduled_jobs.size).to eq(1) end end - end - describe 'enqueue_at' do - it 'calls enqueue_in for you' do + describe "enqueue_at" do + it "calls enqueue_in for you" do freeze_time expect_enqueued_with(job: :process_post, at: 3.hours.from_now) do @@ -169,7 +161,7 @@ RSpec.describe Jobs do end end - it 'handles datetimes that are in the past' do + it "handles datetimes that are in the past" do freeze_time expect_enqueued_with(job: :process_post, at: Time.zone.now) do @@ -177,5 +169,4 @@ RSpec.describe Jobs do end end end - end diff --git a/spec/jobs/mass_award_badge_spec.rb b/spec/jobs/mass_award_badge_spec.rb index 024014a84a9..361dfda270c 100644 --- a/spec/jobs/mass_award_badge_spec.rb +++ b/spec/jobs/mass_award_badge_spec.rb @@ -1,28 +1,35 @@ # frozen_string_literal: true RSpec.describe Jobs::MassAwardBadge do - describe '#execute' do + describe "#execute" do fab!(:badge) { Fabricate(:badge) } fab!(:user) { Fabricate(:user) } - let(:email_mode) { 'email' } + let(:email_mode) { "email" } - it 'creates the badge for an existing user' do + it "creates the badge for an existing user" do execute_job(user) expect(UserBadge.where(user: user, badge: badge).exists?).to eq(true) end - it 'also creates a notification for the user' do + it "also creates a notification for the user" do execute_job(user) expect(Notification.exists?(user: user)).to eq(true) - expect(UserBadge.where.not(notification_id: nil).exists?(user: user, badge: badge)).to eq(true) + expect(UserBadge.where.not(notification_id: nil).exists?(user: user, badge: badge)).to eq( + true, + ) end - it 'updates badge ranks correctly' do + it "updates badge ranks correctly" do user_2 = Fabricate(:user) - UserBadge.create!(badge_id: Badge::Member, user: user, granted_by: Discourse.system_user, granted_at: Time.now) + UserBadge.create!( + badge_id: Badge::Member, + user: user, + granted_by: Discourse.system_user, + granted_at: Time.now, + ) execute_job(user) execute_job(user_2) @@ -31,7 +38,7 @@ RSpec.describe Jobs::MassAwardBadge do expect(UserBadge.find_by(user: user_2, badge: badge).featured_rank).to eq(1) end - it 'grants a badge multiple times to a user' do + it "grants a badge multiple times to a user" do badge.update!(multiple_grant: true) Notification.destroy_all execute_job(user, count: 4, grant_existing_holders: true) @@ -44,7 +51,12 @@ RSpec.describe Jobs::MassAwardBadge do end def execute_job(user, count: 1, grant_existing_holders: false) - subject.execute(user: user.id, badge: badge.id, count: count, grant_existing_holders: grant_existing_holders) + subject.execute( + user: user.id, + badge: badge.id, + count: count, + grant_existing_holders: grant_existing_holders, + ) end end end diff --git a/spec/jobs/migrate_badge_image_to_uploads_spec.rb b/spec/jobs/migrate_badge_image_to_uploads_spec.rb index 21dfabe7806..21c39338ea8 100644 --- a/spec/jobs/migrate_badge_image_to_uploads_spec.rb +++ b/spec/jobs/migrate_badge_image_to_uploads_spec.rb @@ -9,21 +9,18 @@ RSpec.describe Jobs::MigrateBadgeImageToUploads do Rails.logger = @fake_logger = FakeLogger.new end - after do - Rails.logger = @orig_logger - end + after { Rails.logger = @orig_logger } - it 'should migrate to the new badge `image_upload_id` column correctly' do + it "should migrate to the new badge `image_upload_id` column correctly" do stub_request(:get, image_url).to_return( - status: 200, body: file_from_fixtures("smallest.png").read + status: 200, + body: file_from_fixtures("smallest.png").read, ) DB.exec(<<~SQL, flair_url: image_url, id: badge.id) UPDATE badges SET image = :flair_url WHERE id = :id SQL - expect do - described_class.new.execute_onceoff({}) - end.to change { Upload.count }.by(1) + expect do described_class.new.execute_onceoff({}) end.to change { Upload.count }.by(1) badge.reload upload = Upload.last @@ -32,7 +29,7 @@ RSpec.describe Jobs::MigrateBadgeImageToUploads do expect(badge[:image]).to eq(nil) end - it 'should skip badges with invalid flair URLs' do + it "should skip badges with invalid flair URLs" do DB.exec("UPDATE badges SET image = 'abc' WHERE id = ?", badge.id) described_class.new.execute_onceoff({}) expect(@fake_logger.warnings.count).to eq(0) @@ -41,7 +38,7 @@ RSpec.describe Jobs::MigrateBadgeImageToUploads do # this case has a couple of hacks that are needed to test this behavior, so if it # starts failing randomly in the future, I'd just delete it and not bother with it - it 'should not keep retrying forever if download fails' do + it "should not keep retrying forever if download fails" do stub_request(:get, image_url).to_return(status: 403) instance = described_class.new instance.expects(:sleep).times(2) @@ -50,9 +47,7 @@ RSpec.describe Jobs::MigrateBadgeImageToUploads do UPDATE badges SET image = :flair_url WHERE id = :id SQL - expect do - instance.execute_onceoff({}) - end.not_to change { Upload.count } + expect do instance.execute_onceoff({}) end.not_to change { Upload.count } badge.reload expect(badge.image_upload).to eq(nil) diff --git a/spec/jobs/notify_category_change_spec.rb b/spec/jobs/notify_category_change_spec.rb index d218760aed0..567aab68f63 100644 --- a/spec/jobs/notify_category_change_spec.rb +++ b/spec/jobs/notify_category_change_spec.rb @@ -4,13 +4,19 @@ RSpec.describe ::Jobs::NotifyCategoryChange do fab!(:user) { Fabricate(:user) } fab!(:regular_user) { Fabricate(:trust_level_4) } fab!(:post) { Fabricate(:post, user: regular_user) } - fab!(:category) { Fabricate(:category, name: 'test') } + fab!(:category) { Fabricate(:category, name: "test") } it "doesn't create notification for the editor who watches new tag" do - CategoryUser.set_notification_level_for_category(user, CategoryUser.notification_levels[:watching_first_post], category.id) + CategoryUser.set_notification_level_for_category( + user, + CategoryUser.notification_levels[:watching_first_post], + category.id, + ) post.topic.update!(category: category) post.update!(last_editor_id: user.id) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: []) }.not_to change { Notification.count } + expect { described_class.new.execute(post_id: post.id, notified_user_ids: []) }.not_to change { + Notification.count + } end end diff --git a/spec/jobs/notify_mailing_list_subscribers_spec.rb b/spec/jobs/notify_mailing_list_subscribers_spec.rb index ace5161f91d..71cf7347a46 100644 --- a/spec/jobs/notify_mailing_list_subscribers_spec.rb +++ b/spec/jobs/notify_mailing_list_subscribers_spec.rb @@ -3,10 +3,10 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do fab!(:mailing_list_user) { Fabricate(:user) } - before { mailing_list_user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 1) } before do - SiteSetting.tagging_enabled = true + mailing_list_user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 1) end + before { SiteSetting.tagging_enabled = true } fab!(:tag) { Fabricate(:tag) } fab!(:topic) { Fabricate(:topic, tags: [tag]) } @@ -27,10 +27,14 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do end it "triggers :notify_mailing_list_subscribers" do - events = DiscourseEvent.track_events do - Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) - end - expect(events).to include(event_name: :notify_mailing_list_subscribers, params: [[mailing_list_user], post]) + events = + DiscourseEvent.track_events do + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + expect(events).to include( + event_name: :notify_mailing_list_subscribers, + params: [[mailing_list_user], post], + ) end end @@ -133,12 +137,24 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do end context "when from a muted topic" do - before { TopicUser.create(user: mailing_list_user, topic: post.topic, notification_level: TopicUser.notification_levels[:muted]) } + before do + TopicUser.create( + user: mailing_list_user, + topic: post.topic, + notification_level: TopicUser.notification_levels[:muted], + ) + end include_examples "no emails" end context "when from a muted category" do - before { CategoryUser.create(user: mailing_list_user, category: post.topic.category, notification_level: CategoryUser.notification_levels[:muted]) } + before do + CategoryUser.create( + user: mailing_list_user, + category: post.topic.category, + notification_level: CategoryUser.notification_levels[:muted], + ) + end include_examples "no emails" end @@ -150,7 +166,11 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do context "with mute all categories by default setting but user is watching category" do before do SiteSetting.mute_all_categories_by_default = true - CategoryUser.create(user: mailing_list_user, category: post.topic.category, notification_level: CategoryUser.notification_levels[:watching]) + CategoryUser.create( + user: mailing_list_user, + category: post.topic.category, + notification_level: CategoryUser.notification_levels[:watching], + ) end include_examples "one email" end @@ -158,7 +178,11 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do context "with mute all categories by default setting but user is watching tag" do before do SiteSetting.mute_all_categories_by_default = true - TagUser.create(user: mailing_list_user, tag: tag, notification_level: TagUser.notification_levels[:watching]) + TagUser.create( + user: mailing_list_user, + tag: tag, + notification_level: TagUser.notification_levels[:watching], + ) end include_examples "one email" end @@ -166,13 +190,23 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do context "with mute all categories by default setting but user is watching topic" do before do SiteSetting.mute_all_categories_by_default = true - TopicUser.create(user: mailing_list_user, topic: post.topic, notification_level: TopicUser.notification_levels[:watching]) + TopicUser.create( + user: mailing_list_user, + topic: post.topic, + notification_level: TopicUser.notification_levels[:watching], + ) end include_examples "one email" end context "when from a muted tag" do - before { TagUser.create(user: mailing_list_user, tag: tag, notification_level: TagUser.notification_levels[:muted]) } + before do + TagUser.create( + user: mailing_list_user, + tag: tag, + notification_level: TagUser.notification_levels[:muted], + ) + end include_examples "no emails" end @@ -180,44 +214,39 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do before { SiteSetting.max_emails_per_day_per_user = 2 } it "doesn't send any emails" do - (SiteSetting.max_emails_per_day_per_user + 1).times { - mailing_list_user.email_logs.create(email_type: 'foobar', to_address: mailing_list_user.email) - } + (SiteSetting.max_emails_per_day_per_user + 1).times do + mailing_list_user.email_logs.create( + email_type: "foobar", + to_address: mailing_list_user.email, + ) + end expect do - UserNotifications.expects(:mailing_list_notify) - .with(mailing_list_user, post) - .never + UserNotifications.expects(:mailing_list_notify).with(mailing_list_user, post).never - 2.times do - Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) - end + 2.times { Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) } - Jobs::NotifyMailingListSubscribers.new.execute( - post_id: Fabricate(:post, user: user).id - ) + Jobs::NotifyMailingListSubscribers.new.execute(post_id: Fabricate(:post, user: user).id) end.to change { SkippedEmailLog.count }.by(1) - expect(SkippedEmailLog.exists?( - email_type: "mailing_list", - user: mailing_list_user, - post: post, - to_address: mailing_list_user.email, - reason_type: SkippedEmailLog.reason_types[:exceeded_emails_limit] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "mailing_list", + user: mailing_list_user, + post: post, + to_address: mailing_list_user.email, + reason_type: SkippedEmailLog.reason_types[:exceeded_emails_limit], + ), + ).to eq(true) freeze_time(Time.zone.now.tomorrow + 1.second) expect do post = Fabricate(:post, user: user) - UserNotifications.expects(:mailing_list_notify) - .with(mailing_list_user, post) - .once + UserNotifications.expects(:mailing_list_notify).with(mailing_list_user, post).once - Jobs::NotifyMailingListSubscribers.new.execute( - post_id: post.id - ) + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) end.not_to change { SkippedEmailLog.count } end end @@ -229,13 +258,15 @@ RSpec.describe Jobs::NotifyMailingListSubscribers do Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) UserNotifications.expects(:mailing_list_notify).with(mailing_list_user, post).never - expect(SkippedEmailLog.exists?( - email_type: "mailing_list", - user: mailing_list_user, - post: post, - to_address: mailing_list_user.email, - reason_type: SkippedEmailLog.reason_types[:exceeded_bounces_limit] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "mailing_list", + user: mailing_list_user, + post: post, + to_address: mailing_list_user.email, + reason_type: SkippedEmailLog.reason_types[:exceeded_bounces_limit], + ), + ).to eq(true) end end end diff --git a/spec/jobs/notify_moved_posts_spec.rb b/spec/jobs/notify_moved_posts_spec.rb index dda5fdbbe6d..35e40b24e89 100644 --- a/spec/jobs/notify_moved_posts_spec.rb +++ b/spec/jobs/notify_moved_posts_spec.rb @@ -1,33 +1,43 @@ # frozen_string_literal: true RSpec.describe Jobs::NotifyMovedPosts do - it "raises an error without post_ids" do - expect { Jobs::NotifyMovedPosts.new.execute(moved_by_id: 1234) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::NotifyMovedPosts.new.execute(moved_by_id: 1234) }.to raise_error( + Discourse::InvalidParameters, + ) end it "raises an error without moved_by_id" do - expect { Jobs::NotifyMovedPosts.new.execute(post_ids: [1, 2, 3]) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::NotifyMovedPosts.new.execute(post_ids: [1, 2, 3]) }.to raise_error( + Discourse::InvalidParameters, + ) end - context 'with post ids' do + context "with post ids" do fab!(:p1) { Fabricate(:post) } fab!(:p2) { Fabricate(:post, user: Fabricate(:evil_trout), topic: p1.topic) } fab!(:p3) { Fabricate(:post, user: p1.user, topic: p1.topic) } fab!(:admin) { Fabricate(:admin) } - let(:moved_post_notifications) { Notification.where(notification_type: Notification.types[:moved_post]) } + let(:moved_post_notifications) do + Notification.where(notification_type: Notification.types[:moved_post]) + end it "should create two notifications" do - expect { Jobs::NotifyMovedPosts.new.execute(post_ids: [p1.id, p2.id, p3.id], moved_by_id: admin.id) }.to change(moved_post_notifications, :count).by(2) + expect { + Jobs::NotifyMovedPosts.new.execute(post_ids: [p1.id, p2.id, p3.id], moved_by_id: admin.id) + }.to change(moved_post_notifications, :count).by(2) end - context 'when moved by one of the posters' do + context "when moved by one of the posters" do it "create one notifications, because the poster is the mover" do - expect { Jobs::NotifyMovedPosts.new.execute(post_ids: [p1.id, p2.id, p3.id], moved_by_id: p1.user_id) }.to change(moved_post_notifications, :count).by(1) + expect { + Jobs::NotifyMovedPosts.new.execute( + post_ids: [p1.id, p2.id, p3.id], + moved_by_id: p1.user_id, + ) + }.to change(moved_post_notifications, :count).by(1) end end - end - end diff --git a/spec/jobs/notify_reviewable_spec.rb b/spec/jobs/notify_reviewable_spec.rb index fe7c46bd980..4eecaf75e13 100644 --- a/spec/jobs/notify_reviewable_spec.rb +++ b/spec/jobs/notify_reviewable_spec.rb @@ -3,7 +3,7 @@ RSpec.describe Jobs::NotifyReviewable do # remove all the legacy stuff here when redesigned_user_menu_enabled is # removed - describe '#execute' do + describe "#execute" do fab!(:admin) { Fabricate(:admin, moderator: true) } fab!(:moderator) { Fabricate(:moderator) } fab!(:group_user) { Fabricate(:group_user) } @@ -20,9 +20,8 @@ RSpec.describe Jobs::NotifyReviewable do admin_reviewable = Fabricate(:reviewable, reviewable_by_moderator: false) admin.update!(last_seen_reviewable_id: admin_reviewable.id) - messages = MessageBus.track_publish do - described_class.new.execute(reviewable_id: admin_reviewable.id) - end + messages = + MessageBus.track_publish { described_class.new.execute(reviewable_id: admin_reviewable.id) } expect(messages.size).to eq(1) @@ -36,9 +35,10 @@ RSpec.describe Jobs::NotifyReviewable do # Content for moderators moderator_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true) - messages = MessageBus.track_publish do - described_class.new.execute(reviewable_id: moderator_reviewable.id) - end + messages = + MessageBus.track_publish do + described_class.new.execute(reviewable_id: moderator_reviewable.id) + end expect(messages.size).to eq(2) admin_message = messages.find { |m| m.user_ids == [admin.id] } @@ -56,11 +56,11 @@ RSpec.describe Jobs::NotifyReviewable do moderator.update!(last_seen_reviewable_id: moderator_reviewable.id) # Content for a group - group_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group) + group_reviewable = + Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group) - messages = MessageBus.track_publish do - described_class.new.execute(reviewable_id: group_reviewable.id) - end + messages = + MessageBus.track_publish { described_class.new.execute(reviewable_id: group_reviewable.id) } expect(messages.size).to eq(3) @@ -93,9 +93,8 @@ RSpec.describe Jobs::NotifyReviewable do admin_reviewable = Fabricate(:reviewable, reviewable_by_moderator: false) admin.update!(last_seen_reviewable_id: admin_reviewable.id) - messages = MessageBus.track_publish do - described_class.new.execute(reviewable_id: admin_reviewable.id) - end + messages = + MessageBus.track_publish { described_class.new.execute(reviewable_id: admin_reviewable.id) } expect(messages.size).to eq(1) @@ -109,9 +108,10 @@ RSpec.describe Jobs::NotifyReviewable do # Content for moderators moderator_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true) - messages = MessageBus.track_publish do - described_class.new.execute(reviewable_id: moderator_reviewable.id) - end + messages = + MessageBus.track_publish do + described_class.new.execute(reviewable_id: moderator_reviewable.id) + end expect(messages.size).to eq(2) @@ -128,11 +128,11 @@ RSpec.describe Jobs::NotifyReviewable do moderator.update!(last_seen_reviewable_id: moderator_reviewable.id) # Content for a group - group_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group) + group_reviewable = + Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group) - messages = MessageBus.track_publish do - described_class.new.execute(reviewable_id: group_reviewable.id) - end + messages = + MessageBus.track_publish { described_class.new.execute(reviewable_id: group_reviewable.id) } expect(messages.size).to eq(3) @@ -158,9 +158,10 @@ RSpec.describe Jobs::NotifyReviewable do GroupUser.create!(group_id: group.id, user_id: moderator.id) reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group) - messages = MessageBus.track_publish("/reviewable_counts") do - described_class.new.execute(reviewable_id: reviewable.id) - end + messages = + MessageBus.track_publish("/reviewable_counts") do + described_class.new.execute(reviewable_id: reviewable.id) + end group_user_message = messages.find { |m| m.user_ids.include?(user.id) } @@ -171,16 +172,17 @@ RSpec.describe Jobs::NotifyReviewable do SiteSetting.navigation_menu = "legacy" SiteSetting.enable_category_group_moderation = true Reviewable.set_priorities(medium: 2.0) - SiteSetting.reviewable_default_visibility = 'medium' + SiteSetting.reviewable_default_visibility = "medium" GroupUser.create!(group_id: group.id, user_id: moderator.id) # Content for admins only admin_reviewable = Fabricate(:reviewable, reviewable_by_moderator: false) - messages = MessageBus.track_publish("/reviewable_counts") do - described_class.new.execute(reviewable_id: admin_reviewable.id) - end + messages = + MessageBus.track_publish("/reviewable_counts") do + described_class.new.execute(reviewable_id: admin_reviewable.id) + end admin_message = messages.find { |m| m.user_ids.include?(admin.id) } expect(admin_message.data[:reviewable_count]).to eq(0) @@ -188,9 +190,10 @@ RSpec.describe Jobs::NotifyReviewable do # Content for moderators moderator_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true) - messages = MessageBus.track_publish("/reviewable_counts") do - described_class.new.execute(reviewable_id: moderator_reviewable.id) - end + messages = + MessageBus.track_publish("/reviewable_counts") do + described_class.new.execute(reviewable_id: moderator_reviewable.id) + end admin_message = messages.find { |m| m.user_ids.include?(admin.id) } @@ -200,11 +203,13 @@ RSpec.describe Jobs::NotifyReviewable do expect(moderator_message.data[:reviewable_count]).to eq(0) # Content for a group - group_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group) + group_reviewable = + Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group) - messages = MessageBus.track_publish("/reviewable_counts") do - described_class.new.execute(reviewable_id: group_reviewable.id) - end + messages = + MessageBus.track_publish("/reviewable_counts") do + described_class.new.execute(reviewable_id: group_reviewable.id) + end admin_message = messages.find { |m| m.user_ids.include?(admin.id) } expect(admin_message.data[:reviewable_count]).to eq(0) @@ -218,13 +223,14 @@ RSpec.describe Jobs::NotifyReviewable do end end - it 'skips sending notifications if user_ids is empty' do + it "skips sending notifications if user_ids is empty" do reviewable = Fabricate(:reviewable, reviewable_by_moderator: true) regular_user = Fabricate(:user) - messages = MessageBus.track_publish("/reviewable_counts") do - described_class.new.execute(reviewable_id: reviewable.id) - end + messages = + MessageBus.track_publish("/reviewable_counts") do + described_class.new.execute(reviewable_id: reviewable.id) + end expect(messages.size).to eq(0) end diff --git a/spec/jobs/notify_tag_change_spec.rb b/spec/jobs/notify_tag_change_spec.rb index 581fd0e251b..964024f44a2 100644 --- a/spec/jobs/notify_tag_change_spec.rb +++ b/spec/jobs/notify_tag_change_spec.rb @@ -4,20 +4,19 @@ RSpec.describe ::Jobs::NotifyTagChange do fab!(:user) { Fabricate(:user) } fab!(:regular_user) { Fabricate(:trust_level_4) } fab!(:post) { Fabricate(:post, user: regular_user) } - fab!(:tag) { Fabricate(:tag, name: 'test') } + fab!(:tag) { Fabricate(:tag, name: "test") } it "creates notification for watched tag" do TagUser.create!( user_id: user.id, tag_id: tag.id, - notification_level: NotificationLevels.topic_levels[:watching] - ) - TopicTag.create!( - topic_id: post.topic.id, - tag_id: tag.id + notification_level: NotificationLevels.topic_levels[:watching], ) + TopicTag.create!(topic_id: post.topic.id, tag_id: tag.id) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id]) }.to change { Notification.count } + expect { + described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id]) + }.to change { Notification.count } notification = Notification.last expect(notification.user_id).to eq(user.id) expect(notification.topic_id).to eq(post.topic_id) @@ -30,14 +29,13 @@ RSpec.describe ::Jobs::NotifyTagChange do TagUser.create!( user_id: user.id, tag_id: tag.id, - notification_level: NotificationLevels.topic_levels[:watching] - ) - TopicTag.create!( - topic_id: post.topic.id, - tag_id: tag.id + notification_level: NotificationLevels.topic_levels[:watching], ) + TopicTag.create!(topic_id: post.topic.id, tag_id: tag.id) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id]) }.not_to change { Notification.count } + expect { + described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id]) + }.not_to change { Notification.count } end it "doesn't create notification for the editor who watches new tag" do @@ -45,41 +43,78 @@ RSpec.describe ::Jobs::NotifyTagChange do TopicTag.create!(topic: post.topic, tag: tag) post.update!(last_editor_id: user.id) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: []) }.not_to change { Notification.count } + expect { described_class.new.execute(post_id: post.id, notified_user_ids: []) }.not_to change { + Notification.count + } end - it 'doesnt create notification for user watching category' do + it "doesnt create notification for user watching category" do CategoryUser.create!( user_id: user.id, category_id: post.topic.category_id, - notification_level: TopicUser.notification_levels[:watching] + notification_level: TopicUser.notification_levels[:watching], ) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id]) }.not_to change { Notification.count } + expect { + described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id]) + }.not_to change { Notification.count } end - describe 'hidden tag' do - let!(:hidden_group) { Fabricate(:group, name: 'hidden_group') } - let!(:hidden_tag_group) { Fabricate(:tag_group, name: 'hidden', permissions: [[hidden_group.id, :full]]) } - let!(:topic_user) { Fabricate(:topic_user, user: user, topic: post.topic, notification_level: TopicUser.notification_levels[:watching]) } + describe "hidden tag" do + let!(:hidden_group) { Fabricate(:group, name: "hidden_group") } + let!(:hidden_tag_group) do + Fabricate(:tag_group, name: "hidden", permissions: [[hidden_group.id, :full]]) + end + let!(:topic_user) do + Fabricate( + :topic_user, + user: user, + topic: post.topic, + notification_level: TopicUser.notification_levels[:watching], + ) + end - it 'does not create notification for watching user who does not belong to group' do + it "does not create notification for watching user who does not belong to group" do TagGroupMembership.create!(tag_group_id: hidden_tag_group.id, tag_id: tag.id) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id], diff_tags: [tag.name]) }.not_to change { Notification.count } + expect { + described_class.new.execute( + post_id: post.id, + notified_user_ids: [regular_user.id], + diff_tags: [tag.name], + ) + }.not_to change { Notification.count } Fabricate(:group_user, group: hidden_group, user: user) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id], diff_tags: [tag.name]) }.to change { Notification.count } + expect { + described_class.new.execute( + post_id: post.id, + notified_user_ids: [regular_user.id], + diff_tags: [tag.name], + ) + }.to change { Notification.count } end - it 'creates notification when at least added or removed tag is visible to everyone' do - visible_tag = Fabricate(:tag, name: 'visible tag') - visible_group = Fabricate(:tag_group, name: 'visible group') + it "creates notification when at least added or removed tag is visible to everyone" do + visible_tag = Fabricate(:tag, name: "visible tag") + visible_group = Fabricate(:tag_group, name: "visible group") TagGroupMembership.create!(tag_group_id: visible_group.id, tag_id: visible_tag.id) TagGroupMembership.create!(tag_group_id: hidden_tag_group.id, tag_id: tag.id) - expect { described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id], diff_tags: [tag.name]) }.not_to change { Notification.count } - expect { described_class.new.execute(post_id: post.id, notified_user_ids: [regular_user.id], diff_tags: [tag.name, visible_tag.name]) }.to change { Notification.count } + expect { + described_class.new.execute( + post_id: post.id, + notified_user_ids: [regular_user.id], + diff_tags: [tag.name], + ) + }.not_to change { Notification.count } + expect { + described_class.new.execute( + post_id: post.id, + notified_user_ids: [regular_user.id], + diff_tags: [tag.name, visible_tag.name], + ) + }.to change { Notification.count } end end end diff --git a/spec/jobs/old_keys_reminder_spec.rb b/spec/jobs/old_keys_reminder_spec.rb index a6d4fb89db5..f2ccae695d6 100644 --- a/spec/jobs/old_keys_reminder_spec.rb +++ b/spec/jobs/old_keys_reminder_spec.rb @@ -1,28 +1,48 @@ # frozen_string_literal: true RSpec.describe Jobs::OldKeysReminder do - let!(:google_secret) { SiteSetting.create!(name: 'google_oauth2_client_secret', value: '123', data_type: 1) } - let!(:github_secret) { SiteSetting.create!(name: 'github_client_secret', value: '123', data_type: 1) } - let!(:api_key) { Fabricate(:api_key, description: 'api key description') } + let!(:google_secret) do + SiteSetting.create!(name: "google_oauth2_client_secret", value: "123", data_type: 1) + end + let!(:github_secret) do + SiteSetting.create!(name: "github_client_secret", value: "123", data_type: 1) + end + let!(:api_key) { Fabricate(:api_key, description: "api key description") } let!(:admin) { Fabricate(:admin) } let!(:another_admin) { Fabricate(:admin) } - let!(:recent_twitter_secret) { SiteSetting.create!(name: 'twitter_consumer_secret', value: '123', data_type: 1, updated_at: 2.years.from_now) } - let!(:recent_api_key) { Fabricate(:api_key, description: 'recent api key description', created_at: 2.years.from_now, user_id: admin.id) } + let!(:recent_twitter_secret) do + SiteSetting.create!( + name: "twitter_consumer_secret", + value: "123", + data_type: 1, + updated_at: 2.years.from_now, + ) + end + let!(:recent_api_key) do + Fabricate( + :api_key, + description: "recent api key description", + created_at: 2.years.from_now, + user_id: admin.id, + ) + end - it 'is disabled be default' do + it "is disabled be default" do freeze_time 2.years.from_now expect { described_class.new.execute({}) }.not_to change { Post.count } end - it 'sends message to admin with old credentials' do - SiteSetting.send_old_credential_reminder_days = '365' + it "sends message to admin with old credentials" do + SiteSetting.send_old_credential_reminder_days = "365" freeze_time 2.years.from_now expect { described_class.new.execute({}) }.to change { Post.count }.by(1) post = Post.last expect(post.archetype).to eq(Archetype.private_message) - expect(post.topic.topic_allowed_users.map(&:user_id).sort).to eq([Discourse.system_user.id, admin.id, another_admin.id].sort) - expect(post.topic.title).to eq('Reminder about old credentials') + expect(post.topic.topic_allowed_users.map(&:user_id).sort).to eq( + [Discourse.system_user.id, admin.id, another_admin.id].sort, + ) + expect(post.topic.title).to eq("Reminder about old credentials") expect(post.raw).to eq(<<~TEXT.rstrip) Hello! This is a routine yearly security reminder from your Discourse instance. @@ -39,7 +59,7 @@ RSpec.describe Jobs::OldKeysReminder do freeze_time 4.years.from_now described_class.new.execute({}) post = Post.last - expect(post.topic.title).to eq('Reminder about old credentials') + expect(post.topic.title).to eq("Reminder about old credentials") expect(post.raw).to eq(<<~TEXT.rstrip) Hello! This is a routine yearly security reminder from your Discourse instance. @@ -55,15 +75,15 @@ RSpec.describe Jobs::OldKeysReminder do TEXT end - it 'does not send message when send_old_credential_reminder_days is set to 0 or no old keys' do + it "does not send message when send_old_credential_reminder_days is set to 0 or no old keys" do expect { described_class.new.execute({}) }.not_to change { Post.count } - SiteSetting.send_old_credential_reminder_days = '0' + SiteSetting.send_old_credential_reminder_days = "0" freeze_time 2.years.from_now expect { described_class.new.execute({}) }.not_to change { Post.count } end - it 'does not send a message if already exists' do - SiteSetting.send_old_credential_reminder_days = '367' + it "does not send a message if already exists" do + SiteSetting.send_old_credential_reminder_days = "367" freeze_time 2.years.from_now expect { described_class.new.execute({}) }.to change { Post.count }.by(1) Topic.last.trash! diff --git a/spec/jobs/open_topic_spec.rb b/spec/jobs/open_topic_spec.rb index 9a401db7275..fc6bc26bab8 100644 --- a/spec/jobs/open_topic_spec.rb +++ b/spec/jobs/open_topic_spec.rb @@ -3,23 +3,17 @@ RSpec.describe Jobs::OpenTopic do fab!(:admin) { Fabricate(:admin) } - fab!(:topic) do - Fabricate(:topic_timer, user: admin).topic - end + fab!(:topic) { Fabricate(:topic_timer, user: admin).topic } - before do - topic.update!(closed: true) - end + before { topic.update!(closed: true) } - it 'should work' do + it "should work" do freeze_time(61.minutes.from_now) do described_class.new.execute(topic_timer_id: topic.public_topic_timer.id) expect(topic.reload.open?).to eq(true) - expect(Post.last.raw).to eq(I18n.t( - 'topic_statuses.autoclosed_disabled_minutes', count: 61 - )) + expect(Post.last.raw).to eq(I18n.t("topic_statuses.autoclosed_disabled_minutes", count: 61)) end end @@ -31,22 +25,15 @@ RSpec.describe Jobs::OpenTopic do end end - describe 'when category has auto close configured' do + describe "when category has auto close configured" do fab!(:category) do - Fabricate(:category, - auto_close_based_on_last_post: true, - auto_close_hours: 5 - ) + Fabricate(:category, auto_close_based_on_last_post: true, auto_close_hours: 5) end fab!(:topic) { Fabricate(:topic, category: category, closed: true) } it "should restore the category's auto close timer" do - Fabricate(:topic_timer, - status_type: TopicTimer.types[:open], - topic: topic, - user: admin - ) + Fabricate(:topic_timer, status_type: TopicTimer.types[:open], topic: topic, user: admin) freeze_time(61.minutes.from_now) do described_class.new.execute(topic_timer_id: topic.public_topic_timer.id) @@ -61,14 +48,12 @@ RSpec.describe Jobs::OpenTopic do end end - describe 'when user is no longer authorized to open topics' do + describe "when user is no longer authorized to open topics" do fab!(:user) { Fabricate(:user) } - fab!(:topic) do - Fabricate(:topic_timer, user: user).topic - end + fab!(:topic) { Fabricate(:topic_timer, user: user).topic } - it 'should destroy the topic timer' do + it "should destroy the topic timer" do topic.update!(closed: true) freeze_time(topic.public_topic_timer.execute_at + 1.minute) @@ -80,10 +65,7 @@ RSpec.describe Jobs::OpenTopic do end it "should reconfigure topic timer if category's topics are set to autoclose" do - category = Fabricate(:category, - auto_close_based_on_last_post: true, - auto_close_hours: 5 - ) + category = Fabricate(:category, auto_close_based_on_last_post: true, auto_close_hours: 5) topic = Fabricate(:topic, category: category) topic.public_topic_timer.update!(user: user) @@ -92,12 +74,10 @@ RSpec.describe Jobs::OpenTopic do freeze_time(topic.public_topic_timer.execute_at + 1.minute) expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) - end.to change { topic.reload.public_topic_timer.user }.from(user).to(Discourse.system_user) - .and change { topic.public_topic_timer.id } + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) + end.to change { topic.reload.public_topic_timer.user }.from(user).to( + Discourse.system_user, + ).and change { topic.public_topic_timer.id } expect(topic.reload.closed).to eq(false) end diff --git a/spec/jobs/pending_queued_posts_reminder_spec.rb b/spec/jobs/pending_queued_posts_reminder_spec.rb index 7611556dd5f..0d33627c040 100644 --- a/spec/jobs/pending_queued_posts_reminder_spec.rb +++ b/spec/jobs/pending_queued_posts_reminder_spec.rb @@ -8,9 +8,7 @@ RSpec.describe Jobs::PendingQueuedPostsReminder do it "never emails" do described_class.any_instance.expects(:should_notify_ids).never - expect { - job.execute({}) - }.to_not change { Post.count } + expect { job.execute({}) }.to_not change { Post.count } end end @@ -25,37 +23,35 @@ RSpec.describe Jobs::PendingQueuedPostsReminder do Fabricate(:reviewable_queued_post, created_at: 14.minutes.ago) # expect 16 minute post to be picked up but not 14 min post expect { job.execute({}) }.to change { Post.count }.by(1) - expect(Topic.where( - subtype: TopicSubtype.system_message, - title: I18n.t('system_messages.queued_posts_reminder.subject_template', count: 1) - ).exists?).to eq(true) + expect( + Topic.where( + subtype: TopicSubtype.system_message, + title: I18n.t("system_messages.queued_posts_reminder.subject_template", count: 1), + ).exists?, + ).to eq(true) end end context "when notify_about_queued_posts_after is 24" do - before do - SiteSetting.notify_about_queued_posts_after = 24 - end + before { SiteSetting.notify_about_queued_posts_after = 24 } context "when we haven't been notified in a while" do - before do - job.last_notified_id = nil - end + before { job.last_notified_id = nil } it "doesn't create system message if there are no queued posts" do - expect { - job.execute({}) - }.to_not change { Post.count } + expect { job.execute({}) }.to_not change { Post.count } end it "creates system message if there are new queued posts" do Fabricate(:reviewable_queued_post, created_at: 48.hours.ago) Fabricate(:reviewable_queued_post, created_at: 45.hours.ago) expect { job.execute({}) }.to change { Post.count }.by(1) - expect(Topic.where( - subtype: TopicSubtype.system_message, - title: I18n.t('system_messages.queued_posts_reminder.subject_template', count: 2) - ).exists?).to eq(true) + expect( + Topic.where( + subtype: TopicSubtype.system_message, + title: I18n.t("system_messages.queued_posts_reminder.subject_template", count: 2), + ).exists?, + ).to eq(true) end end diff --git a/spec/jobs/pending_reviewables_reminder_spec.rb b/spec/jobs/pending_reviewables_reminder_spec.rb index 2b6d4deafd7..bb3fa0f4c76 100644 --- a/spec/jobs/pending_reviewables_reminder_spec.rb +++ b/spec/jobs/pending_reviewables_reminder_spec.rb @@ -4,7 +4,12 @@ RSpec.describe Jobs::PendingReviewablesReminder do let(:job) { described_class.new } def create_flag(created_at) - PostActionCreator.create(Fabricate(:user), Fabricate(:post), :spam, created_at: created_at).reviewable + PostActionCreator.create( + Fabricate(:user), + Fabricate(:post), + :spam, + created_at: created_at, + ).reviewable end def execute @@ -44,9 +49,7 @@ RSpec.describe Jobs::PendingReviewablesReminder do described_class.clear_key end - after do - described_class.clear_key - end + after { described_class.clear_key } it "doesn't send message when flags are less than 48 hours old" do create_flag(47.hours.ago) @@ -76,27 +79,30 @@ RSpec.describe Jobs::PendingReviewablesReminder do it "doesn't send a message when `reviewable_default_visibility` is not met" do Reviewable.set_priorities(medium: 3.0) - SiteSetting.reviewable_default_visibility = 'medium' + SiteSetting.reviewable_default_visibility = "medium" expect(execute.sent_reminder).to eq(false) end it "sends a message when `reviewable_default_visibility` is met" do Reviewable.set_priorities(medium: 2.0) - SiteSetting.reviewable_default_visibility = 'medium' + SiteSetting.reviewable_default_visibility = "medium" expect(execute.sent_reminder).to eq(true) end end - it 'deletes previous messages' do + it "deletes previous messages" do GroupMessage.create( - Group[:moderators].name, 'reviewables_reminder', - { limit_once_per: false, message_params: { mentions: '', count: 1 } } + Group[:moderators].name, + "reviewables_reminder", + { limit_once_per: false, message_params: { mentions: "", count: 1 } }, ) create_flag(49.hours.ago) execute - expect(Topic.where(title: I18n.t("system_messages.reviewables_reminder.subject_template")).count).to eq(1) + expect( + Topic.where(title: I18n.t("system_messages.reviewables_reminder.subject_template")).count, + ).to eq(1) end end end diff --git a/spec/jobs/pending_users_reminder_spec.rb b/spec/jobs/pending_users_reminder_spec.rb index c482231d6e7..bb2e32c6b0d 100644 --- a/spec/jobs/pending_users_reminder_spec.rb +++ b/spec/jobs/pending_users_reminder_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Jobs::PendingUsersReminder do - context 'when must_approve_users is true' do + context "when must_approve_users is true" do before do SiteSetting.must_approve_users = true Jobs::PendingUsersReminder.any_instance.stubs(:previous_newest_username).returns(nil) @@ -42,16 +42,17 @@ RSpec.describe Jobs::PendingUsersReminder do it "sets the correct pending user count in the notification" do SiteSetting.pending_users_reminder_delay_minutes = 8 Fabricate(:user, created_at: 9.minutes.ago) - PostCreator.expects(:create).with(Discourse.system_user, has_entries(title: '1 user waiting for approval')) + PostCreator.expects(:create).with( + Discourse.system_user, + has_entries(title: "1 user waiting for approval"), + ) Jobs::PendingUsersReminder.new.execute({}) end end end - context 'when must_approve_users is false' do - before do - SiteSetting.must_approve_users = false - end + context "when must_approve_users is false" do + before { SiteSetting.must_approve_users = false } it "doesn't send a message to anyone when there are pending users" do AdminUserIndexQuery.any_instance.stubs(:find_users_query).returns(stub_everything(count: 1)) diff --git a/spec/jobs/periodical_updates_spec.rb b/spec/jobs/periodical_updates_spec.rb index f6282fb956f..3b0749eb4e4 100644 --- a/spec/jobs/periodical_updates_spec.rb +++ b/spec/jobs/periodical_updates_spec.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true RSpec.describe Jobs::PeriodicalUpdates do - it "works" do - # does not blow up, no mocks, everything is called Jobs::PeriodicalUpdates.new.execute(nil) end diff --git a/spec/jobs/poll_mailbox_spec.rb b/spec/jobs/poll_mailbox_spec.rb index 3ba8fe72fec..ab6162e60b1 100644 --- a/spec/jobs/poll_mailbox_spec.rb +++ b/spec/jobs/poll_mailbox_spec.rb @@ -20,8 +20,7 @@ RSpec.describe Jobs::PollMailbox do describe ".poll_pop3" do # the date is dynamic here because there is a 1 week cutoff for # the pop3 polling - let(:example_email) do - email = <<~EMAIL + let(:example_email) { email = <<~EMAIL } Return-Path: From: One To: team@bar.com @@ -34,27 +33,22 @@ RSpec.describe Jobs::PollMailbox do This is an email example. EMAIL - end context "with pop errors" do - before do - Discourse.expects(:handle_job_exception).at_least_once - end + before { Discourse.expects(:handle_job_exception).at_least_once } - after do - Discourse.redis.del(Jobs::PollMailbox::POLL_MAILBOX_TIMEOUT_ERROR_KEY) - end + after { Discourse.redis.del(Jobs::PollMailbox::POLL_MAILBOX_TIMEOUT_ERROR_KEY) } it "add an admin dashboard message on pop authentication error" do - Net::POP3.any_instance.expects(:start) - .raises(Net::POPAuthenticationError.new).at_least_once + Net::POP3.any_instance.expects(:start).raises(Net::POPAuthenticationError.new).at_least_once poller.poll_pop3 - i18n_key = 'dashboard.poll_pop3_auth_error' + i18n_key = "dashboard.poll_pop3_auth_error" - expect(AdminDashboardData.problem_message_check(i18n_key)) - .to eq(I18n.t(i18n_key, base_path: Discourse.base_path)) + expect(AdminDashboardData.problem_message_check(i18n_key)).to eq( + I18n.t(i18n_key, base_path: Discourse.base_path), + ) end it "logs an error on pop connection timeout error" do @@ -62,10 +56,11 @@ RSpec.describe Jobs::PollMailbox do 4.times { poller.poll_pop3 } - i18n_key = 'dashboard.poll_pop3_timeout' + i18n_key = "dashboard.poll_pop3_timeout" - expect(AdminDashboardData.problem_message_check(i18n_key)) - .to eq(I18n.t(i18n_key, base_path: Discourse.base_path)) + expect(AdminDashboardData.problem_message_check(i18n_key)).to eq( + I18n.t(i18n_key, base_path: Discourse.base_path), + ) end it "logs an error when pop fails and continues with next message" do @@ -91,7 +86,14 @@ RSpec.describe Jobs::PollMailbox do SiteSetting.pop3_polling_delete_from_server = true - poller.expects(:mail_too_old?).returns(false).then.raises(RuntimeError).then.returns(false).times(3) + poller + .expects(:mail_too_old?) + .returns(false) + .then + .raises(RuntimeError) + .then + .returns(false) + .times(3) poller.expects(:process_popmail).times(2) poller.poll_pop3 end @@ -162,12 +164,14 @@ RSpec.describe Jobs::PollMailbox do end it "does not reply to a bounced email" do - expect { process_popmail(:bounced_email) }.to_not change { ActionMailer::Base.deliveries.count } + expect { process_popmail(:bounced_email) }.to_not change { + ActionMailer::Base.deliveries.count + } incoming_email = IncomingEmail.last expect(incoming_email.rejection_message).to eq( - I18n.t("emails.incoming.errors.bounced_email_error") + I18n.t("emails.incoming.errors.bounced_email_error"), ) end end diff --git a/spec/jobs/post_update_topic_tracking_state_spec.rb b/spec/jobs/post_update_topic_tracking_state_spec.rb index 3bbed8a1ef3..442f5574018 100644 --- a/spec/jobs/post_update_topic_tracking_state_spec.rb +++ b/spec/jobs/post_update_topic_tracking_state_spec.rb @@ -3,12 +3,12 @@ RSpec.describe Jobs::PostUpdateTopicTrackingState do fab!(:post) { Fabricate(:post) } - it 'should publish messages' do + it "should publish messages" do messages = MessageBus.track_publish { subject.execute({ post_id: post.id }) } expect(messages.size).not_to eq(0) end - it 'should not publish messages for deleted topics' do + it "should not publish messages for deleted topics" do post.topic.trash! messages = MessageBus.track_publish { subject.execute({ post_id: post.id }) } expect(messages.size).to eq(0) diff --git a/spec/jobs/post_uploads_recovery_spec.rb b/spec/jobs/post_uploads_recovery_spec.rb index 58ab91a5065..57c7633406e 100644 --- a/spec/jobs/post_uploads_recovery_spec.rb +++ b/spec/jobs/post_uploads_recovery_spec.rb @@ -1,15 +1,13 @@ # frozen_string_literal: true RSpec.describe Jobs::PostUploadsRecovery do - describe '#grace_period' do - it 'should restrict the grace period to the right range' do - SiteSetting.purge_deleted_uploads_grace_period_days = - described_class::MIN_PERIOD - 1 + describe "#grace_period" do + it "should restrict the grace period to the right range" do + SiteSetting.purge_deleted_uploads_grace_period_days = described_class::MIN_PERIOD - 1 expect(described_class.new.grace_period).to eq(30) - SiteSetting.purge_deleted_uploads_grace_period_days = - described_class::MAX_PERIOD + 1 + SiteSetting.purge_deleted_uploads_grace_period_days = described_class::MAX_PERIOD + 1 expect(described_class.new.grace_period).to eq(120) end diff --git a/spec/jobs/problem_checks_spec.rb b/spec/jobs/problem_checks_spec.rb index 7f254f001d7..39144f64fcf 100644 --- a/spec/jobs/problem_checks_spec.rb +++ b/spec/jobs/problem_checks_spec.rb @@ -22,7 +22,11 @@ RSpec.describe Jobs::ProblemChecks do AdminDashboardData.add_scheduled_problem_check(:test_identifier) do [ AdminDashboardData::Problem.new("big problem"), - AdminDashboardData::Problem.new("yuge problem", priority: "high", identifier: "config_is_a_mess") + AdminDashboardData::Problem.new( + "yuge problem", + priority: "high", + identifier: "config_is_a_mess", + ), ] end @@ -34,8 +38,16 @@ RSpec.describe Jobs::ProblemChecks do it "does not add the same problem twice if the identifier already exists" do AdminDashboardData.add_scheduled_problem_check(:test_identifier) do [ - AdminDashboardData::Problem.new("yuge problem", priority: "high", identifier: "config_is_a_mess"), - AdminDashboardData::Problem.new("nasty problem", priority: "high", identifier: "config_is_a_mess") + AdminDashboardData::Problem.new( + "yuge problem", + priority: "high", + identifier: "config_is_a_mess", + ), + AdminDashboardData::Problem.new( + "nasty problem", + priority: "high", + identifier: "config_is_a_mess", + ), ] end diff --git a/spec/jobs/process_bulk_invite_emails_spec.rb b/spec/jobs/process_bulk_invite_emails_spec.rb index a177e244b80..021dcb0b34e 100644 --- a/spec/jobs/process_bulk_invite_emails_spec.rb +++ b/spec/jobs/process_bulk_invite_emails_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true RSpec.describe Jobs::ProcessBulkInviteEmails do - describe '#execute' do - it 'processes pending invites' do + describe "#execute" do + it "processes pending invites" do invite = Fabricate(:invite, emailed_status: Invite.emailed_status_types[:bulk_pending]) described_class.new.execute({}) diff --git a/spec/jobs/process_email_spec.rb b/spec/jobs/process_email_spec.rb index eeedaf8ace3..a6240d44648 100644 --- a/spec/jobs/process_email_spec.rb +++ b/spec/jobs/process_email_spec.rb @@ -1,12 +1,10 @@ # frozen_string_literal: true RSpec.describe Jobs::ProcessEmail do - let(:mail) { "From: foo@bar.com\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?" } it "process an email without retry" do Email::Processor.expects(:process!).with(mail, retry_on_rate_limit: false, source: nil) Jobs::ProcessEmail.new.execute(mail: mail) end - end diff --git a/spec/jobs/process_post_spec.rb b/spec/jobs/process_post_spec.rb index 98d843fcdc7..d7f35b3ff14 100644 --- a/spec/jobs/process_post_spec.rb +++ b/spec/jobs/process_post_spec.rb @@ -5,12 +5,12 @@ RSpec.describe Jobs::ProcessPost do expect { Jobs::ProcessPost.new.perform(post_id: 1, sync_exec: true) }.not_to raise_error end - context 'with a post' do + context "with a post" do fab!(:post) { Fabricate(:post) } - it 'does not erase posts when CookedPostProcessor malfunctions' do + it "does not erase posts when CookedPostProcessor malfunctions" do # Look kids, an actual reason why you want to use mocks - CookedPostProcessor.any_instance.expects(:html).returns(' ') + CookedPostProcessor.any_instance.expects(:html).returns(" ") cooked = post.cooked post.reload @@ -19,7 +19,7 @@ RSpec.describe Jobs::ProcessPost do Jobs::ProcessPost.new.execute(post_id: post.id, cook: true) end - it 'recooks if needed' do + it "recooks if needed" do cooked = post.cooked post.update_columns(cooked: "frogs") @@ -29,8 +29,9 @@ RSpec.describe Jobs::ProcessPost do expect(post.cooked).to eq(cooked) end - it 'processes posts' do - post = Fabricate(:post, raw: "") + it "processes posts" do + post = + Fabricate(:post, raw: "") expect(post.cooked).to match(/http/) stub_image_size @@ -47,8 +48,16 @@ RSpec.describe Jobs::ProcessPost do end it "extracts links to quoted posts" do - quoted_post = Fabricate(:post, raw: "This is a post with a link to https://www.discourse.org", post_number: 42) - post.update_columns(raw: "This quote is the best\n\n[quote=\"#{quoted_post.user.username}, topic:#{quoted_post.topic_id}, post:#{quoted_post.post_number}\"]\n#{quoted_post.excerpt}\n[/quote]") + quoted_post = + Fabricate( + :post, + raw: "This is a post with a link to https://www.discourse.org", + post_number: 42, + ) + post.update_columns( + raw: + "This quote is the best\n\n[quote=\"#{quoted_post.user.username}, topic:#{quoted_post.topic_id}, post:#{quoted_post.post_number}\"]\n#{quoted_post.excerpt}\n[/quote]", + ) stub_image_size # when creating a quote, we also create the reflexion link expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { TopicLink.count }.by(2) @@ -102,9 +111,7 @@ RSpec.describe Jobs::ProcessPost do end context "when download_remote_images_to_local? is enabled" do - before do - SiteSetting.download_remote_images_to_local = true - end + before { SiteSetting.download_remote_images_to_local = true } it "enqueues" do expect_enqueued_with(job: :pull_hotlinked_images, args: { post_id: post.id }) do @@ -117,6 +124,5 @@ RSpec.describe Jobs::ProcessPost do expect(Jobs::PullHotlinkedImages.jobs.size).to eq(0) end end - end end diff --git a/spec/jobs/process_shelved_notifications_spec.rb b/spec/jobs/process_shelved_notifications_spec.rb index fb100d36da7..f0e419f733c 100644 --- a/spec/jobs/process_shelved_notifications_spec.rb +++ b/spec/jobs/process_shelved_notifications_spec.rb @@ -8,16 +8,22 @@ RSpec.describe Jobs::ProcessShelvedNotifications do future = Fabricate(:do_not_disturb_timing, ends_at: 1.day.from_now) past = Fabricate(:do_not_disturb_timing, starts_at: 2.day.ago, ends_at: 1.minute.ago) - expect { - subject.execute({}) - }.to change { DoNotDisturbTiming.count }.by (-1) + expect { subject.execute({}) }.to change { DoNotDisturbTiming.count }.by (-1) expect(DoNotDisturbTiming.find_by(id: future.id)).to eq(future) expect(DoNotDisturbTiming.find_by(id: past.id)).to eq(nil) end it "does not process shelved_notifications when the user is in DND" do user.do_not_disturb_timings.create(starts_at: 2.days.ago, ends_at: 2.days.from_now) - notification = Notification.create(read: false, user_id: user.id, topic_id: 2, post_number: 1, data: '{}', notification_type: 1) + notification = + Notification.create( + read: false, + user_id: user.id, + topic_id: 2, + post_number: 1, + data: "{}", + notification_type: 1, + ) expect(notification.shelved_notification).to be_present subject.execute({}) expect(notification.shelved_notification).to be_present @@ -25,7 +31,15 @@ RSpec.describe Jobs::ProcessShelvedNotifications do it "processes and destroys shelved_notifications when the user leaves DND" do user.do_not_disturb_timings.create(starts_at: 2.days.ago, ends_at: 2.days.from_now) - notification = Notification.create(read: false, user_id: user.id, topic_id: 2, post_number: 1, data: '{}', notification_type: 1) + notification = + Notification.create( + read: false, + user_id: user.id, + topic_id: 2, + post_number: 1, + data: "{}", + notification_type: 1, + ) user.do_not_disturb_timings.last.update(ends_at: 1.days.ago) expect(notification.shelved_notification).to be_present diff --git a/spec/jobs/publish_topic_to_category_spec.rb b/spec/jobs/publish_topic_to_category_spec.rb index 0f5c213f19e..76320055615 100644 --- a/spec/jobs/publish_topic_to_category_spec.rb +++ b/spec/jobs/publish_topic_to_category_spec.rb @@ -7,12 +7,13 @@ RSpec.describe Jobs::PublishTopicToCategory do let(:topic) do topic = Fabricate(:topic, category: category) - Fabricate(:topic_timer, + Fabricate( + :topic_timer, status_type: TopicTimer.types[:publish_to_category], category_id: another_category.id, topic: topic, execute_at: 1.minute.ago, - created_at: 5.minutes.ago + created_at: 5.minutes.ago, ) Fabricate(:post, topic: topic, user: topic.user) @@ -20,8 +21,8 @@ RSpec.describe Jobs::PublishTopicToCategory do topic end - describe 'when topic has been deleted' do - it 'should not publish the topic to the new category' do + describe "when topic has been deleted" do + it "should not publish the topic to the new category" do created_at = freeze_time 1.hour.ago topic @@ -36,18 +37,17 @@ RSpec.describe Jobs::PublishTopicToCategory do end end - it 'should publish the topic to the new category' do + it "should publish the topic to the new category" do freeze_time 1.hour.ago do topic.update!(visible: false) end now = freeze_time - message = MessageBus.track_publish do - described_class.new.execute(topic_timer_id: topic.public_topic_timer.id) - end.find do |m| - Hash === m.data && m.data.key?(:reload_topic) && m.data.key?(:refresh_stream) - end + message = + MessageBus + .track_publish { described_class.new.execute(topic_timer_id: topic.public_topic_timer.id) } + .find { |m| Hash === m.data && m.data.key?(:reload_topic) && m.data.key?(:refresh_stream) } topic.reload expect(topic.category).to eq(another_category) @@ -55,32 +55,36 @@ RSpec.describe Jobs::PublishTopicToCategory do expect(topic.public_topic_timer).to eq(nil) expect(message.channel).to eq("/topic/#{topic.id}") - %w{created_at bumped_at updated_at last_posted_at}.each do |attribute| + %w[created_at bumped_at updated_at last_posted_at].each do |attribute| expect(topic.public_send(attribute)).to eq_time(now) end end - describe 'when topic is a private message' do - it 'should publish the topic to the new category' do + describe "when topic is a private message" do + it "should publish the topic to the new category" do freeze_time 1.hour.ago do - expect { topic.convert_to_private_message(Discourse.system_user) } - .to change { topic.private_message? }.to(true) + expect { topic.convert_to_private_message(Discourse.system_user) }.to change { + topic.private_message? + }.to(true) end topic.allowed_users << topic.public_topic_timer.user now = freeze_time - message = MessageBus.track_publish do - described_class.new.execute(topic_timer_id: topic.public_topic_timer.id) - end.last + message = + MessageBus + .track_publish do + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id) + end + .last topic.reload expect(topic.category).to eq(another_category) expect(topic.visible).to eq(true) expect(topic.private_message?).to eq(false) - %w{created_at bumped_at updated_at last_posted_at}.each do |attribute| + %w[created_at bumped_at updated_at last_posted_at].each do |attribute| expect(topic.public_send(attribute)).to eq_time(now) end @@ -118,8 +122,8 @@ RSpec.describe Jobs::PublishTopicToCategory do end end - describe 'when new category has a default auto-close' do - it 'should apply the auto-close timer upon publishing' do + describe "when new category has a default auto-close" do + it "should apply the auto-close timer upon publishing" do freeze_time another_category.update!(auto_close_hours: 5) diff --git a/spec/jobs/pull_hotlinked_images_spec.rb b/spec/jobs/pull_hotlinked_images_spec.rb index b16915ae428..3f842c275c1 100644 --- a/spec/jobs/pull_hotlinked_images_spec.rb +++ b/spec/jobs/pull_hotlinked_images_spec.rb @@ -5,26 +5,44 @@ RSpec.describe Jobs::PullHotlinkedImages do let(:broken_image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat2.png" } let(:large_image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat3.png" } let(:encoded_image_url) { "https://example.com/אלחוט-.jpg" } - let(:png) { Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") } - let(:large_png) { Base64.decode64("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAK10lEQVR42r3aeVRTVx4H8Oc2atWO7Sw9OnM6HWvrOON0aFlcAZ3RopZWOyqgoCACKqPWBUVQi4gIqAVllciiKPu+JOyGnQQSNgkIIQgoKljAYVARCZnf4yXhkeXlJmDP+f4hOUF+n3fvffe++y5W0i4qJqWoDU8hKQUPxWFKcq9VnHxJ8gTi5EqS0yJOtiRZfHEyJWE0i0MnJaMJTzopaQ/wpJKS0ogneTQYABANTDlDvpxBCsiu72eUP0zPq8Fzr45e8TircRDFQAAy5ABpcgDCgJV2iCbRQM+rinU/E26ie9NgfrDO1GBtTBy96SH/WhBhaxwfGEjndmfKGeiaGsYAJXIANQyCkfR05u3dhuOKVhLamnmRzocyKp9mNo9QG9IRDDiAiMaG3Nqfo45aoJROzk3DDxNCbjGahBM0yAKoDfIDOpNZE/bNYrVKJyfylB2D91pdA3lAjwE0MDAyS+BCalw9kdu2xvT6AY0NWBkJoNaAzsrj4CN1YtUTidi/hdH4BvGmJGPAAYgGMuMery/U6ONJqZ5I1PlTjNExre7kgJU/EqEbJC0gjDpiiv9hnSkJ2z+t9dzxwNcSUudlUuuxnXP+W/bZTWWO64uO6hccWQ0pPm4IP1a6GFe5bYXvNF7f0xxg3XrzgCDYjn1m4+218/D/SndaYnSqBpMDDlDXkHYnMlh7Srj+HLanxfOsyyOVN0ScYI0zkOeVZvYZGEI2/DFDMkWgTw7jAGWUA5owMOt7QtcvDF09qybA/mGC6zA7aCLVExkq9U3895/wm9LpgyonBxmDGKDQoHBySPQ8B5e/zM2kJdalN/fqxKsn8oLhFr5mdvDyX6UVNqqcpMmDAWNJACjtUMDrDVn7m6SdS/kxPwrizg+zAycLAKm5tA0a4a7DPpSFhmIAxWAgDKm0IJrutBr/g3D5n9E9J7F6oiNFGf2WtnI2vboH3YADEA0AuG2ml2i2BC4/AAYKr00uAHL/ihk0QnxQMPqKFWM/FiEamFWPYMHD8tgF1UMmZfjKZLDIJ1z/vQibzTKrbop2wAGIhoxbt8IN5zZHnoHqO5LdJr16IkXHDG4afJDJG0B8chADUAxxTnbp1trE5Z/0ASDN09hTcJdLy+EoawQZgyyAwhCxcznr0k4C0JNz5R0BYFqM3PBhQugtxKdQrEICUGFoE4ZtWPAg4jQBeJHv/Y4AkBKHdTHuZ8lP0hSDAQdQGwhAUUNv4s6/EvcfSD/T590B2u8cj3SwltkNUGaQBSgbDAXc9pxTW4jqIf8ruAa37efJLg/DfuBd21ftYU7OA387+QXSk2gHWMmRw/M2F9D2d8WffsW8Sv5+X/mtyBN7s+V2NBQasMpOEYqhuLG3MimMqL4h/GTu4fW01b/z05qrMKEGC96W+8sA8g/qKX281JuWafX350lniG++rIpOTcknb8lQGHAAoqG+pgqqr7hqE2K4kCg0bO3CJDMthvVKInTrlUmm/4j+9vO7mxYNlfrJAJiHVsYaL0g1XZy194scmy+JMCyXxWz+CAD4anTFjLrLpiMVQW+4t1G2lQiDGIBiuF/NLbmwM1B3PpQe892SFtqh4fIAhZ14mBUo34WE7ECFC29hRdDz5LO5dtrwdAGM0pP/HKoMzWsZRtwakwVQGPJjo/2/ej9Q74N8xy19o+tQYcWNzjT3mJNmR/W/uPi9fobr3ifpl6hXeG9Zge1JF5LPWvz4zYoTa7VSzu0mniggMEigNcBQ7GjE5A9Kt/eoOxLGkQBUGkoyGeEbPqnys2+OPlcbdir80PdOX+usmDFdG8OIwCc3bI0vm657WeSrsPouhuelbQZh/9nqY7FB+lsGc2ad27w86oTJo5SLrwu9s/dpVXuYFPEHELcocQC1QXpjhS4EpcMwiPhh2/U9XzfedYYFhe7UKdJSqkNOIt4oMy/uIwP68n6C3/WzMmIFHIUeJawMLm7ul9lmVdYOYgCKob6aK72NEo8yQ+UBtl99BkXoTMFcv1sF3UNaIpd24vCqvykDvCr2PbJ6GQFwNtKFrjhuCHFCCvmvcuW2ihUaMO4TWYCyAU0GSJcSsCblRTjDSJAZoFnuNiafLqReMrQlukKTylQvBZC3iikMOIDCQGaQAT9nq1gLqQRQBABFLa9U7tcTBjEApR3IALh1/DIAlQZZAIWBDOjO9HrXAMT3JliVBKCyHciALsYvAUAx4IAqOYDCmxKPBFD5QDNBQHHLS2XvfmQMYgCKgQx4muGhFmCw1B8dIOTQyvj9FO+vyDclrPqpLECZgVczBoAlA3URMCubLv6D9I657ZOP0lws1QJQv4OTGnAAogEdAF+A+TXHw3b0R5qoszLLyx4+gc8RAeUt/SrfIxIGMYDCoBDwONVdaQ9mB+3XWeK87kvJ1EYTDfYLn9XDgsdO+3NYKSACUN6FQsYAKg2IgIqgY6tnzmi6bP8y2X2EmGUbkkWCPJitV82cURfuqPq5nhPM4vchvpDGauQAygxkAMW+ULCdsfWSj/tCTr8IdeqPdBnK94FnFCEr8DXd68CyRXeObkfpRWx+D+JLdRxANlC0QwMaINHZfP37c4oczQkDnjDnvlCnMuc9RvPnxp/ehQKokAAoOlIeGUDdDvKAtsQLyv72mzJ/P6uN+rNnHtf5S7GjRVeQQ6nTbge9pdB/vEzWDso9aqoEUBuw2mciZY0gY0AEEBHEuZzZqAdFG743c/n0aQ7rtBruOKO/y+HwnyMebsABiIbG2jFAa7wryh4bPDaUXD+swWuoKv5TxMMNYgCFgQSoIgHOv7uNLbgLcfldiAc0xgAqDbVtLwTJXgQAeojmLzLKAzjBxyl257vqcgsfChUeDJA3YHUkgEpDQz2vJU7cCDJTEnQSWOHBDK0wMACgL0U7mLptXWO/fGmCk7myGW2gOra09Q36aSUcoIahc4Rfmi59JBi3H5j3k5fJOs8dhgoTYL0Jqi/1PfyMTrUKHOKGcwS9Kg9okA1iALqh+tGggBFIGJRtn2gWWEHwmlsRD5lIDdj9LpG8gXpyuN/yRJBwEQCwRYWytkEcuB28iuK2EXVPXOEAqaEW2dBUzZI+HE/wTT2RnjpGSZtQg1NjYoDa7dA50sKMIgywyTPB6l9VRbPaXmt28m0MQNEOCgdDbXu/IM17tCO5TaQjveWG1Qi6NT75htWTAOoaeA/4gnhXlF0Wiq7f3NSk1okrGQMO0NzQOdLMziU60usSPw2q7+SVlnWMlE3g1BjG6xZNxFDe1s2OO0Z0JHhxBuMBJlroUSgju682ldUxTH24QaVhDFAvB1Bp4HS+PRO/5ZDP7xtjnaXLJGKlBMtVeGqDuRk2If97z/tl0XVYZg+T3nF0F3tcjN1W2vFWrdNK8gYcgGiQvykFFl7a7oFBvG5o5UfvVRQrRuQu+mjgH5lRu7JjLPISLAtTrJ1pf94dj4U0+mhw4opsEAPU6kiEIZ1XYnZlFgFQKzu8MYtYzKYUs63E7Lnz0ls5iKeVFBrGAGq1A6uj1zZw0XZPzPwuZhqE7biiqm4vzNQP/7JVFmZbgdlxxnKienFBe4/G7YA1kADI7TDilmQJZVlE41cRirBlYdZMzIqB7UnGdseRkohZZmDW+ZhNmfibEHvuzAOcaWTD5XpLuBepdfKtiAxQ1xDPTdnhOdXUH7Nlj7uWKDnAme7bvPlI1a/Hfz4ljp+BfnqPPKD/DzQWIVWNoUiJAAAAAElFTkSuQmCC") } + let(:png) do + Base64.decode64( + "R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==", + ) + end + let(:large_png) do + Base64.decode64( + "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAK10lEQVR42r3aeVRTVx4H8Oc2atWO7Sw9OnM6HWvrOON0aFlcAZ3RopZWOyqgoCACKqPWBUVQi4gIqAVllciiKPu+JOyGnQQSNgkIIQgoKljAYVARCZnf4yXhkeXlJmDP+f4hOUF+n3fvffe++y5W0i4qJqWoDU8hKQUPxWFKcq9VnHxJ8gTi5EqS0yJOtiRZfHEyJWE0i0MnJaMJTzopaQ/wpJKS0ogneTQYABANTDlDvpxBCsiu72eUP0zPq8Fzr45e8TircRDFQAAy5ABpcgDCgJV2iCbRQM+rinU/E26ie9NgfrDO1GBtTBy96SH/WhBhaxwfGEjndmfKGeiaGsYAJXIANQyCkfR05u3dhuOKVhLamnmRzocyKp9mNo9QG9IRDDiAiMaG3Nqfo45aoJROzk3DDxNCbjGahBM0yAKoDfIDOpNZE/bNYrVKJyfylB2D91pdA3lAjwE0MDAyS+BCalw9kdu2xvT6AY0NWBkJoNaAzsrj4CN1YtUTidi/hdH4BvGmJGPAAYgGMuMery/U6ONJqZ5I1PlTjNExre7kgJU/EqEbJC0gjDpiiv9hnSkJ2z+t9dzxwNcSUudlUuuxnXP+W/bZTWWO64uO6hccWQ0pPm4IP1a6GFe5bYXvNF7f0xxg3XrzgCDYjn1m4+218/D/SndaYnSqBpMDDlDXkHYnMlh7Srj+HLanxfOsyyOVN0ScYI0zkOeVZvYZGEI2/DFDMkWgTw7jAGWUA5owMOt7QtcvDF09qybA/mGC6zA7aCLVExkq9U3895/wm9LpgyonBxmDGKDQoHBySPQ8B5e/zM2kJdalN/fqxKsn8oLhFr5mdvDyX6UVNqqcpMmDAWNJACjtUMDrDVn7m6SdS/kxPwrizg+zAycLAKm5tA0a4a7DPpSFhmIAxWAgDKm0IJrutBr/g3D5n9E9J7F6oiNFGf2WtnI2vboH3YADEA0AuG2ml2i2BC4/AAYKr00uAHL/ihk0QnxQMPqKFWM/FiEamFWPYMHD8tgF1UMmZfjKZLDIJ1z/vQibzTKrbop2wAGIhoxbt8IN5zZHnoHqO5LdJr16IkXHDG4afJDJG0B8chADUAxxTnbp1trE5Z/0ASDN09hTcJdLy+EoawQZgyyAwhCxcznr0k4C0JNz5R0BYFqM3PBhQugtxKdQrEICUGFoE4ZtWPAg4jQBeJHv/Y4AkBKHdTHuZ8lP0hSDAQdQGwhAUUNv4s6/EvcfSD/T590B2u8cj3SwltkNUGaQBSgbDAXc9pxTW4jqIf8ruAa37efJLg/DfuBd21ftYU7OA387+QXSk2gHWMmRw/M2F9D2d8WffsW8Sv5+X/mtyBN7s+V2NBQasMpOEYqhuLG3MimMqL4h/GTu4fW01b/z05qrMKEGC96W+8sA8g/qKX281JuWafX350lniG++rIpOTcknb8lQGHAAoqG+pgqqr7hqE2K4kCg0bO3CJDMthvVKInTrlUmm/4j+9vO7mxYNlfrJAJiHVsYaL0g1XZy194scmy+JMCyXxWz+CAD4anTFjLrLpiMVQW+4t1G2lQiDGIBiuF/NLbmwM1B3PpQe892SFtqh4fIAhZ14mBUo34WE7ECFC29hRdDz5LO5dtrwdAGM0pP/HKoMzWsZRtwakwVQGPJjo/2/ej9Q74N8xy19o+tQYcWNzjT3mJNmR/W/uPi9fobr3ifpl6hXeG9Zge1JF5LPWvz4zYoTa7VSzu0mniggMEigNcBQ7GjE5A9Kt/eoOxLGkQBUGkoyGeEbPqnys2+OPlcbdir80PdOX+usmDFdG8OIwCc3bI0vm657WeSrsPouhuelbQZh/9nqY7FB+lsGc2ad27w86oTJo5SLrwu9s/dpVXuYFPEHELcocQC1QXpjhS4EpcMwiPhh2/U9XzfedYYFhe7UKdJSqkNOIt4oMy/uIwP68n6C3/WzMmIFHIUeJawMLm7ul9lmVdYOYgCKob6aK72NEo8yQ+UBtl99BkXoTMFcv1sF3UNaIpd24vCqvykDvCr2PbJ6GQFwNtKFrjhuCHFCCvmvcuW2ihUaMO4TWYCyAU0GSJcSsCblRTjDSJAZoFnuNiafLqReMrQlukKTylQvBZC3iikMOIDCQGaQAT9nq1gLqQRQBABFLa9U7tcTBjEApR3IALh1/DIAlQZZAIWBDOjO9HrXAMT3JliVBKCyHciALsYvAUAx4IAqOYDCmxKPBFD5QDNBQHHLS2XvfmQMYgCKgQx4muGhFmCw1B8dIOTQyvj9FO+vyDclrPqpLECZgVczBoAlA3URMCubLv6D9I657ZOP0lws1QJQv4OTGnAAogEdAF+A+TXHw3b0R5qoszLLyx4+gc8RAeUt/SrfIxIGMYDCoBDwONVdaQ9mB+3XWeK87kvJ1EYTDfYLn9XDgsdO+3NYKSACUN6FQsYAKg2IgIqgY6tnzmi6bP8y2X2EmGUbkkWCPJitV82cURfuqPq5nhPM4vchvpDGauQAygxkAMW+ULCdsfWSj/tCTr8IdeqPdBnK94FnFCEr8DXd68CyRXeObkfpRWx+D+JLdRxANlC0QwMaINHZfP37c4oczQkDnjDnvlCnMuc9RvPnxp/ehQKokAAoOlIeGUDdDvKAtsQLyv72mzJ/P6uN+rNnHtf5S7GjRVeQQ6nTbge9pdB/vEzWDso9aqoEUBuw2mciZY0gY0AEEBHEuZzZqAdFG743c/n0aQ7rtBruOKO/y+HwnyMebsABiIbG2jFAa7wryh4bPDaUXD+swWuoKv5TxMMNYgCFgQSoIgHOv7uNLbgLcfldiAc0xgAqDbVtLwTJXgQAeojmLzLKAzjBxyl257vqcgsfChUeDJA3YHUkgEpDQz2vJU7cCDJTEnQSWOHBDK0wMACgL0U7mLptXWO/fGmCk7myGW2gOra09Q36aSUcoIahc4Rfmi59JBi3H5j3k5fJOs8dhgoTYL0Jqi/1PfyMTrUKHOKGcwS9Kg9okA1iALqh+tGggBFIGJRtn2gWWEHwmlsRD5lIDdj9LpG8gXpyuN/yRJBwEQCwRYWytkEcuB28iuK2EXVPXOEAqaEW2dBUzZI+HE/wTT2RnjpGSZtQg1NjYoDa7dA50sKMIgywyTPB6l9VRbPaXmt28m0MQNEOCgdDbXu/IM17tCO5TaQjveWG1Qi6NT75htWTAOoaeA/4gnhXlF0Wiq7f3NSk1okrGQMO0NzQOdLMziU60usSPw2q7+SVlnWMlE3g1BjG6xZNxFDe1s2OO0Z0JHhxBuMBJlroUSgju682ldUxTH24QaVhDFAvB1Bp4HS+PRO/5ZDP7xtjnaXLJGKlBMtVeGqDuRk2If97z/tl0XVYZg+T3nF0F3tcjN1W2vFWrdNK8gYcgGiQvykFFl7a7oFBvG5o5UfvVRQrRuQu+mjgH5lRu7JjLPISLAtTrJ1pf94dj4U0+mhw4opsEAPU6kiEIZ1XYnZlFgFQKzu8MYtYzKYUs63E7Lnz0ls5iKeVFBrGAGq1A6uj1zZw0XZPzPwuZhqE7biiqm4vzNQP/7JVFmZbgdlxxnKienFBe4/G7YA1kADI7TDilmQJZVlE41cRirBlYdZMzIqB7UnGdseRkohZZmDW+ZhNmfibEHvuzAOcaWTD5XpLuBepdfKtiAxQ1xDPTdnhOdXUH7Nlj7uWKDnAme7bvPlI1a/Hfz4ljp+BfnqPPKD/DzQWIVWNoUiJAAAAAElFTkSuQmCC", + ) + end let(:upload_path) { Discourse.store.upload_path } before do Jobs.run_immediately! stub_request(:get, image_url).to_return(body: png, headers: { "Content-Type" => "image/png" }) - stub_request(:get, encoded_image_url).to_return(body: png, headers: { "Content-Type" => "image/png" }) + stub_request(:get, encoded_image_url).to_return( + body: png, + headers: { + "Content-Type" => "image/png", + }, + ) stub_request(:get, broken_image_url).to_return(status: 404) - stub_request(:get, large_image_url).to_return(body: large_png, headers: { "Content-Type" => "image/png" }) - - stub_request( - :get, - "#{Discourse.base_url}/#{upload_path}/original/1X/f59ea56fe8ebe42048491d43a19d9f34c5d0f8dc.gif" + stub_request(:get, large_image_url).to_return( + body: large_png, + headers: { + "Content-Type" => "image/png", + }, ) stub_request( :get, - "#{Discourse.base_url}/#{upload_path}/original/1X/c530c06cf89c410c0355d7852644a73fc3ec8c04.png" + "#{Discourse.base_url}/#{upload_path}/original/1X/f59ea56fe8ebe42048491d43a19d9f34c5d0f8dc.gif", + ) + + stub_request( + :get, + "#{Discourse.base_url}/#{upload_path}/original/1X/c530c06cf89c410c0355d7852644a73fc3ec8c04.png", ) SiteSetting.download_remote_images_to_local = true @@ -32,22 +50,20 @@ RSpec.describe Jobs::PullHotlinkedImages do SiteSetting.download_remote_images_threshold = 0 end - describe '#execute' do - before do - Jobs.run_immediately! - end + describe "#execute" do + before { Jobs.run_immediately! } - it 'does nothing if topic has been deleted' do + it "does nothing if topic has been deleted" do post = Fabricate(:post, raw: "") post.topic.destroy! - expect do - Jobs::PullHotlinkedImages.new.execute(post_id: post.id) - end.not_to change { Upload.count } + expect do Jobs::PullHotlinkedImages.new.execute(post_id: post.id) end.not_to change { + Upload.count + } end - it 'does nothing if there are no large images to pull' do - post = Fabricate(:post, raw: 'bob bob') + it "does nothing if there are no large images to pull" do + post = Fabricate(:post, raw: "bob bob") orig = post.updated_at freeze_time 1.week.from_now @@ -55,18 +71,18 @@ RSpec.describe Jobs::PullHotlinkedImages do expect(orig).to eq_time(post.reload.updated_at) end - it 'replaces images' do + it "replaces images" do post = Fabricate(:post, raw: "") stub_image_size - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) - .and not_change { UserHistory.count } # Should not add to the staff log + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1).and not_change { UserHistory.count } # Should not add to the staff log expect(post.reload.raw).to eq("") end - it 'enqueues raw replacement job with a delay' do + it "enqueues raw replacement job with a delay" do Jobs.run_later! post = Fabricate(:post, raw: "") @@ -76,12 +92,16 @@ RSpec.describe Jobs::PullHotlinkedImages do Jobs.expects(:cancel_scheduled_job).with(:update_hotlinked_raw, post_id: post.id).once delay = SiteSetting.editing_grace_period + 1 - expect_enqueued_with(job: :update_hotlinked_raw, args: { post_id: post.id }, at: Time.zone.now + delay.seconds) do - Jobs::PullHotlinkedImages.new.execute(post_id: post.id) - end + expect_enqueued_with( + job: :update_hotlinked_raw, + args: { + post_id: post.id, + }, + at: Time.zone.now + delay.seconds, + ) { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } end - it 'removes downloaded images when they are no longer needed' do + it "removes downloaded images when they are no longer needed" do post = Fabricate(:post, raw: "") stub_image_size post.rebake! @@ -94,43 +114,41 @@ RSpec.describe Jobs::PullHotlinkedImages do expect(post.upload_references.count).to eq(0) end - it 'replaces images again after edit' do + it "replaces images again after edit" do post = Fabricate(:post, raw: "") stub_image_size - expect do - post.rebake! - end.to change { Upload.count }.by(1) + expect do post.rebake! end.to change { Upload.count }.by(1) expect(post.reload.raw).to eq("") # Post raw is updated back to the old value (e.g. by wordpress integration) post.update(raw: "") - expect do - post.rebake! - end.not_to change { Upload.count } # We alread have the upload + expect do post.rebake! end.not_to change { Upload.count } # We alread have the upload expect(post.reload.raw).to eq("") end - it 'replaces encoded image urls' do + it "replaces encoded image urls" do post = Fabricate(:post, raw: "") stub_image_size - expect do - Jobs::PullHotlinkedImages.new.execute(post_id: post.id) - end.to change { Upload.count }.by(1) + expect do Jobs::PullHotlinkedImages.new.execute(post_id: post.id) end.to change { + Upload.count + }.by(1) expect(post.reload.raw).to eq("") end - it 'replaces images in an anchor tag with weird indentation' do + it "replaces images in an anchor tag with weird indentation" do # Skipped pending https://meta.discourse.org/t/152801 # This spec was previously passing, even though the resulting markdown was invalid # Now the spec has been improved, and shows the issue - stub_request(:get, "http://test.localhost/uploads/short-url/z2QSs1KJWoj51uYhDjb6ifCzxH6.gif") - .to_return(status: 200, body: "") + stub_request( + :get, + "http://test.localhost/uploads/short-url/z2QSs1KJWoj51uYhDjb6ifCzxH6.gif", + ).to_return(status: 200, body: "") post = Fabricate(:post, raw: <<~MD)

@@ -139,9 +157,9 @@ RSpec.describe Jobs::PullHotlinkedImages do MD - expect do - Jobs::PullHotlinkedImages.new.execute(post_id: post.id) - end.to change { Upload.count }.by(1) + expect do Jobs::PullHotlinkedImages.new.execute(post_id: post.id) end.to change { + Upload.count + }.by(1) upload = post.uploads.last @@ -153,55 +171,55 @@ RSpec.describe Jobs::PullHotlinkedImages do MD end - it 'replaces correct image URL' do - url = image_url.sub("/2e/Longcat1.png", '') + it "replaces correct image URL" do + url = image_url.sub("/2e/Longcat1.png", "") post = Fabricate(:post, raw: "[Images](#{url})\n![](#{image_url})") stub_image_size - expect do - Jobs::PullHotlinkedImages.new.execute(post_id: post.id) - end.to change { Upload.count }.by(1) + expect do Jobs::PullHotlinkedImages.new.execute(post_id: post.id) end.to change { + Upload.count + }.by(1) expect(post.reload.raw).to eq("[Images](#{url})\n![](#{Upload.last.short_url})") end - it 'replaces images without protocol' do - url = image_url.sub(/^https?\:/, '') + it "replaces images without protocol" do + url = image_url.sub(/^https?\:/, "") post = Fabricate(:post, raw: "test") stub_image_size - expect do - Jobs::PullHotlinkedImages.new.execute(post_id: post.id) - end.to change { Upload.count }.by(1) + expect do Jobs::PullHotlinkedImages.new.execute(post_id: post.id) end.to change { + Upload.count + }.by(1) expect(post.reload.raw).to eq("\"test\"") end - it 'replaces images without extension' do - url = image_url.sub(/\.[a-zA-Z0-9]+$/, '') + it "replaces images without extension" do + url = image_url.sub(/\.[a-zA-Z0-9]+$/, "") stub_request(:get, url).to_return(body: png, headers: { "Content-Type" => "image/png" }) post = Fabricate(:post, raw: "") stub_image_size - expect do - Jobs::PullHotlinkedImages.new.execute(post_id: post.id) - end.to change { Upload.count }.by(1) + expect do Jobs::PullHotlinkedImages.new.execute(post_id: post.id) end.to change { + Upload.count + }.by(1) expect(post.reload.raw).to eq("") end - it 'replaces optimized images' do + it "replaces optimized images" do optimized_image = Fabricate(:optimized_image) url = "#{Discourse.base_url}#{optimized_image.url}" - stub_request(:get, url) - .to_return(status: 200, body: file_from_fixtures("smallest.png")) + stub_request(:get, url).to_return(status: 200, body: file_from_fixtures("smallest.png")) post = Fabricate(:post, raw: "") stub_image_size - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1) upload = Upload.last post.reload @@ -233,8 +251,9 @@ RSpec.describe Jobs::PullHotlinkedImages do url = Discourse.base_url + url post = Fabricate(:post, raw: "") upload.update(access_control_post: post) - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .not_to change { Upload.count } + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.not_to change { + Upload.count + } end context "when the upload original_sha1 is missing" do @@ -253,8 +272,9 @@ RSpec.describe Jobs::PullHotlinkedImages do # without this we get an infinite hang... Post.any_instance.stubs(:trigger_post_process) - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1) end end @@ -272,16 +292,18 @@ RSpec.describe Jobs::PullHotlinkedImages do upload.update(access_control_post: Fabricate(:post)) FileStore::S3Store.any_instance.stubs(:store_upload).returns(upload.url) - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1) - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .not_to change { Upload.count } + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.not_to change { + Upload.count + } end end end - it 'replaces markdown image' do + it "replaces markdown image" do post = Fabricate(:post, raw: <<~MD) [![some test](#{image_url})](https://somelink.com) ![some test](#{image_url}) @@ -291,8 +313,9 @@ RSpec.describe Jobs::PullHotlinkedImages do MD stub_image_size - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1) post.reload @@ -305,18 +328,19 @@ RSpec.describe Jobs::PullHotlinkedImages do MD end - it 'works when invalid url in post' do + it "works when invalid url in post" do post = Fabricate(:post, raw: <<~MD) ![some test](#{image_url}) ![some test 2]("#{image_url}) MD stub_image_size - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1) end - it 'replaces bbcode images' do + it "replaces bbcode images" do post = Fabricate(:post, raw: <<~MD) [img] #{image_url} @@ -328,8 +352,9 @@ RSpec.describe Jobs::PullHotlinkedImages do MD stub_image_size - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1) post.reload @@ -340,17 +365,21 @@ RSpec.describe Jobs::PullHotlinkedImages do MD end - describe 'onebox' do + describe "onebox" do let(:media) { "File:Brisbane_May_2013201.jpg" } let(:url) { "https://commons.wikimedia.org/wiki/#{media}" } - let(:api_url) { "https://en.wikipedia.org/w/api.php?action=query&titles=#{media}&prop=imageinfo&iilimit=50&iiprop=timestamp|user|url&iiurlwidth=500&format=json" } + let(:api_url) do + "https://en.wikipedia.org/w/api.php?action=query&titles=#{media}&prop=imageinfo&iilimit=50&iiprop=timestamp|user|url&iiurlwidth=500&format=json" + end before do stub_request(:head, url) - stub_request(:get, url).to_return(body: '') + stub_request(:get, url).to_return(body: "") stub_request(:head, image_url) - stub_request(:get, api_url).to_return(body: "{ + stub_request(:get, api_url).to_return( + body: + "{ \"query\": { \"pages\": { \"-1\": { @@ -363,21 +392,22 @@ RSpec.describe Jobs::PullHotlinkedImages do } } } - }") + }", + ) end - it 'replaces image src' do + it "replaces image src" do post = Fabricate(:post, raw: "#{url}") stub_image_size post.rebake! post.reload - expect(post.cooked).to match(/ #{url} @@ -402,9 +432,7 @@ RSpec.describe Jobs::PullHotlinkedImages do MD stub_image_size - 2.times do - post.rebake! - end + 2.times { post.rebake! } post.reload @@ -416,13 +444,13 @@ RSpec.describe Jobs::PullHotlinkedImages do ![](upload://z2QSs1KJWoj51uYhDjb6ifCzxH6.gif) MD - expect(post.cooked).to match(/

/) end - it 'rewrites a lone onebox' do + it "rewrites a lone onebox" do post = Fabricate(:post, raw: <<~MD) Onebox here: #{image_url} @@ -438,35 +466,35 @@ RSpec.describe Jobs::PullHotlinkedImages do ![](upload://z2QSs1KJWoj51uYhDjb6ifCzxH6.gif) MD - expect(post.cooked).to match(/", - title: "Some title that is long enough" - ) + it "replaces missing local uploads in lightbox link" do + post = + PostCreator.create!( + user, + raw: "", + title: "Some title that is long enough", + ) expect(post.reload.cooked).to have_tag(:a, with: { class: "lightbox" }) - stub_request(:get, "#{Discourse.base_url}#{upload.url}") - .to_return(status: 200, body: file_from_fixtures("smallest.png")) + stub_request(:get, "#{Discourse.base_url}#{upload.url}").to_return( + status: 200, + body: file_from_fixtures("smallest.png"), + ) upload.delete - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .to change { Upload.count }.by(1) + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.to change { + Upload.count + }.by(1) post.reload @@ -572,16 +600,18 @@ RSpec.describe Jobs::PullHotlinkedImages do end it "doesn't remove optimized images from lightboxes" do - post = PostCreator.create!( - user, - raw: "![alt](#{upload.short_url})", - title: "Some title that is long enough" - ) + post = + PostCreator.create!( + user, + raw: "![alt](#{upload.short_url})", + title: "Some title that is long enough", + ) expect(post.reload.cooked).to have_tag(:a, with: { class: "lightbox" }) - expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) } - .not_to change { Upload.count } + expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.not_to change { + Upload.count + } post.reload @@ -605,12 +635,14 @@ RSpec.describe Jobs::PullHotlinkedImages do end context "when there's not enough disk space" do - before { SiteSetting.download_remote_images_threshold = 75 } it "disables download_remote_images_threshold and send a notification to the admin" do StaffActionLogger.any_instance.expects(:log_site_setting_change).once - SystemMessage.expects(:create_from_system_user).with(Discourse.site_contact_user, :download_remote_images_disabled).once + SystemMessage + .expects(:create_from_system_user) + .with(Discourse.site_contact_user, :download_remote_images_disabled) + .once job.execute({ post_id: post.id }) expect(SiteSetting.download_remote_images_to_local).to eq(false) @@ -622,12 +654,14 @@ RSpec.describe Jobs::PullHotlinkedImages do expect(SiteSetting.download_remote_images_to_local).to eq(true) end - end end def stub_s3(upload) stub_upload(upload) - stub_request(:get, "https:" + upload.url).to_return(status: 200, body: file_from_fixtures("smallest.png")) + stub_request(:get, "https:" + upload.url).to_return( + status: 200, + body: file_from_fixtures("smallest.png"), + ) end end diff --git a/spec/jobs/pull_user_profile_hotlinked_images_spec.rb b/spec/jobs/pull_user_profile_hotlinked_images_spec.rb index 2dbce8437fd..de96bf09afe 100644 --- a/spec/jobs/pull_user_profile_hotlinked_images_spec.rb +++ b/spec/jobs/pull_user_profile_hotlinked_images_spec.rb @@ -4,26 +4,32 @@ RSpec.describe Jobs::PullUserProfileHotlinkedImages do fab!(:user) { Fabricate(:user) } let(:image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat1.png" } - let(:png) { Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") } + let(:png) do + Base64.decode64( + "R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==", + ) + end before do stub_request(:get, image_url).to_return(body: png, headers: { "Content-Type" => "image/png" }) SiteSetting.download_remote_images_to_local = true end - describe '#execute' do - before do - stub_image_size - end + describe "#execute" do + before { stub_image_size } - it 'replaces images' do + it "replaces images" do user.user_profile.update!(bio_raw: "![](#{image_url})") - expect { Jobs::PullUserProfileHotlinkedImages.new.execute(user_id: user.id) }.to change { Upload.count }.by(1) + expect { Jobs::PullUserProfileHotlinkedImages.new.execute(user_id: user.id) }.to change { + Upload.count + }.by(1) expect(user.user_profile.reload.bio_cooked).to include(Upload.last.url) end - it 'handles nil bio' do - expect { Jobs::PullUserProfileHotlinkedImages.new.execute(user_id: user.id) }.not_to change { Upload.count } + it "handles nil bio" do + expect { Jobs::PullUserProfileHotlinkedImages.new.execute(user_id: user.id) }.not_to change { + Upload.count + } expect(user.user_profile.reload.bio_cooked).to eq(nil) end end diff --git a/spec/jobs/rebake_custom_emoji_posts_spec.rb b/spec/jobs/rebake_custom_emoji_posts_spec.rb index 5f64ba824d1..49951a4660e 100644 --- a/spec/jobs/rebake_custom_emoji_posts_spec.rb +++ b/spec/jobs/rebake_custom_emoji_posts_spec.rb @@ -1,20 +1,20 @@ # frozen_string_literal: true RSpec.describe Jobs::RebakeCustomEmojiPosts do - it 'should rebake posts that are using a given custom emoji' do + it "should rebake posts that are using a given custom emoji" do upload = Fabricate(:upload) - custom_emoji = CustomEmoji.create!(name: 'test', upload: upload) + custom_emoji = CustomEmoji.create!(name: "test", upload: upload) Emoji.clear_cache - post = Fabricate(:post, raw: 'some post with :test: yay') + post = Fabricate(:post, raw: "some post with :test: yay") expect(post.reload.cooked).to eq( - "

some post with \":test:\" yay

" + "

some post with \":test:\" yay

", ) custom_emoji.destroy! Emoji.clear_cache - described_class.new.execute(name: 'test') + described_class.new.execute(name: "test") - expect(post.reload.cooked).to eq('

some post with :test: yay

') + expect(post.reload.cooked).to eq("

some post with :test: yay

") end end diff --git a/spec/jobs/refresh_users_reviewable_counts_spec.rb b/spec/jobs/refresh_users_reviewable_counts_spec.rb index 7437996391a..667ca6c0389 100644 --- a/spec/jobs/refresh_users_reviewable_counts_spec.rb +++ b/spec/jobs/refresh_users_reviewable_counts_spec.rb @@ -7,7 +7,9 @@ RSpec.describe Jobs::RefreshUsersReviewableCounts do fab!(:group) { Fabricate(:group) } fab!(:topic) { Fabricate(:topic) } - fab!(:reviewable1) { Fabricate(:reviewable, reviewable_by_group: group, reviewable_by_moderator: true, topic: topic) } + fab!(:reviewable1) do + Fabricate(:reviewable, reviewable_by_group: group, reviewable_by_moderator: true, topic: topic) + end fab!(:reviewable2) { Fabricate(:reviewable, reviewable_by_moderator: false) } fab!(:reviewable3) { Fabricate(:reviewable, reviewable_by_moderator: true) } @@ -19,11 +21,12 @@ RSpec.describe Jobs::RefreshUsersReviewableCounts do Group.refresh_automatic_groups! end - describe '#execute' do + describe "#execute" do it "publishes reviewable counts for the members of the specified groups" do - messages = MessageBus.track_publish do - described_class.new.execute(group_ids: [Group::AUTO_GROUPS[:staff]]) - end + messages = + MessageBus.track_publish do + described_class.new.execute(group_ids: [Group::AUTO_GROUPS[:staff]]) + end expect(messages.size).to eq(2) moderator_message = messages.find { |m| m.user_ids == [moderator.id] } @@ -32,9 +35,7 @@ RSpec.describe Jobs::RefreshUsersReviewableCounts do admin_message = messages.find { |m| m.user_ids == [admin.id] } expect(moderator_message.channel).to eq("/reviewable_counts/#{moderator.id}") - messages = MessageBus.track_publish do - described_class.new.execute(group_ids: [group.id]) - end + messages = MessageBus.track_publish { described_class.new.execute(group_ids: [group.id]) } expect(messages.size).to eq(1) user_message = messages.find { |m| m.user_ids == [user.id] } @@ -42,9 +43,10 @@ RSpec.describe Jobs::RefreshUsersReviewableCounts do end it "published counts respect reviewables visibility" do - messages = MessageBus.track_publish do - described_class.new.execute(group_ids: [Group::AUTO_GROUPS[:staff], group.id]) - end + messages = + MessageBus.track_publish do + described_class.new.execute(group_ids: [Group::AUTO_GROUPS[:staff], group.id]) + end expect(messages.size).to eq(3) admin_message = messages.find { |m| m.user_ids == [admin.id] } @@ -52,22 +54,13 @@ RSpec.describe Jobs::RefreshUsersReviewableCounts do user_message = messages.find { |m| m.user_ids == [user.id] } expect(admin_message.channel).to eq("/reviewable_counts/#{admin.id}") - expect(admin_message.data).to eq( - reviewable_count: 3, - unseen_reviewable_count: 3 - ) + expect(admin_message.data).to eq(reviewable_count: 3, unseen_reviewable_count: 3) expect(moderator_message.channel).to eq("/reviewable_counts/#{moderator.id}") - expect(moderator_message.data).to eq( - reviewable_count: 2, - unseen_reviewable_count: 2 - ) + expect(moderator_message.data).to eq(reviewable_count: 2, unseen_reviewable_count: 2) expect(user_message.channel).to eq("/reviewable_counts/#{user.id}") - expect(user_message.data).to eq( - reviewable_count: 1, - unseen_reviewable_count: 1 - ) + expect(user_message.data).to eq(reviewable_count: 1, unseen_reviewable_count: 1) end end end diff --git a/spec/jobs/regular/bulk_user_title_update_spec.rb b/spec/jobs/regular/bulk_user_title_update_spec.rb index fa6c9b8da1b..ebe66c0e4de 100644 --- a/spec/jobs/regular/bulk_user_title_update_spec.rb +++ b/spec/jobs/regular/bulk_user_title_update_spec.rb @@ -1,33 +1,37 @@ # frozen_string_literal: true RSpec.describe Jobs::BulkUserTitleUpdate do - fab!(:badge) { Fabricate(:badge, name: 'Protector of the Realm', allow_title: true) } + fab!(:badge) { Fabricate(:badge, name: "Protector of the Realm", allow_title: true) } fab!(:user) { Fabricate(:user) } fab!(:other_user) { Fabricate(:user) } - describe 'update action' do + describe "update action" do before do BadgeGranter.grant(badge, user) user.update(title: badge.name) end - it 'updates the title of all users with the attached granted title badge id on their profile' do + it "updates the title of all users with the attached granted title badge id on their profile" do execute_update - expect(user.reload.title).to eq('King of the Forum') + expect(user.reload.title).to eq("King of the Forum") end - it 'does not set the title for any other users' do + it "does not set the title for any other users" do execute_update - expect(other_user.reload.title).not_to eq('King of the Forum') + expect(other_user.reload.title).not_to eq("King of the Forum") end def execute_update - described_class.new.execute(new_title: 'King of the Forum', granted_badge_id: badge.id, action: described_class::UPDATE_ACTION) + described_class.new.execute( + new_title: "King of the Forum", + granted_badge_id: badge.id, + action: described_class::UPDATE_ACTION, + ) end end - describe 'reset action' do - let(:customized_badge_name) { 'Merit Badge' } + describe "reset action" do + let(:customized_badge_name) { "Merit Badge" } before do TranslationOverride.upsert!(I18n.locale, Badge.i18n_key(badge.name), customized_badge_name) @@ -35,14 +39,12 @@ RSpec.describe Jobs::BulkUserTitleUpdate do user.update(title: customized_badge_name) end - it 'updates the title of all users back to the original badge name' do + it "updates the title of all users back to the original badge name" do expect(user.reload.title).to eq(customized_badge_name) described_class.new.execute(granted_badge_id: badge.id, action: described_class::RESET_ACTION) - expect(user.reload.title).to eq('Protector of the Realm') + expect(user.reload.title).to eq("Protector of the Realm") end - after do - TranslationOverride.revert!(I18n.locale, Badge.i18n_key(badge.name)) - end + after { TranslationOverride.revert!(I18n.locale, Badge.i18n_key(badge.name)) } end end diff --git a/spec/jobs/regular/group_smtp_email_spec.rb b/spec/jobs/regular/group_smtp_email_spec.rb index d01ce2c02d8..475e939d9b1 100644 --- a/spec/jobs/regular/group_smtp_email_spec.rb +++ b/spec/jobs/regular/group_smtp_email_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Jobs::GroupSmtpEmail do group_id: group.id, post_id: post_id, email: "test@test.com", - cc_emails: ["otherguy@test.com", "cormac@lit.com"] + cc_emails: %w[otherguy@test.com cormac@lit.com], } end let(:staged1) { Fabricate(:staged, email: "otherguy@test.com") } @@ -51,14 +51,20 @@ RSpec.describe Jobs::GroupSmtpEmail do it "includes a 'reply above this line' message" do subject.execute(args) - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) - expect(email_log.as_mail_message.html_part.to_s).to include(I18n.t("user_notifications.reply_above_line")) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + expect(email_log.as_mail_message.html_part.to_s).to include( + I18n.t("user_notifications.reply_above_line"), + ) end it "does not include context posts" do subject.execute(args) - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) - expect(email_log.as_mail_message.text_part.to_s).not_to include(I18n.t("user_notifications.previous_discussion")) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + expect(email_log.as_mail_message.text_part.to_s).not_to include( + I18n.t("user_notifications.previous_discussion"), + ) expect(email_log.as_mail_message.text_part.to_s).not_to include("some first post content") end @@ -67,14 +73,20 @@ RSpec.describe Jobs::GroupSmtpEmail do post.update!(reply_to_post_number: 1, reply_to_user: second_post.user) PostReply.create(post: second_post, reply: post) subject.execute(args) - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) - expect(email_log.raw_headers).to include("In-Reply-To: ") - expect(email_log.as_mail_message.html_part.to_s).not_to include(I18n.t("user_notifications.in_reply_to")) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + expect(email_log.raw_headers).to include( + "In-Reply-To: ", + ) + expect(email_log.as_mail_message.html_part.to_s).not_to include( + I18n.t("user_notifications.in_reply_to"), + ) end it "includes the participants in the correct format (but not the recipient user), and does not have links for the staged users" do subject.execute(args) - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) email_text = email_log.as_mail_message.text_part.to_s expect(email_text).to include("Support Group") expect(email_text).to include("otherguy@test.com") @@ -87,7 +99,8 @@ RSpec.describe Jobs::GroupSmtpEmail do it "creates an EmailLog record with the correct details" do subject.execute(args) - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) expect(email_log).not_to eq(nil) expect(email_log.message_id).to eq("discourse/post/#{post.id}@test.localhost") end @@ -96,7 +109,8 @@ RSpec.describe Jobs::GroupSmtpEmail do subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.last.subject).to eq("Re: Help I need support") - incoming_email = IncomingEmail.find_by(post_id: post.id, topic_id: post.topic_id, user_id: post.user.id) + incoming_email = + IncomingEmail.find_by(post_id: post.id, topic_id: post.topic_id, user_id: post.user.id) expect(incoming_email).not_to eq(nil) expect(incoming_email.message_id).to eq("discourse/post/#{post.id}@test.localhost") expect(incoming_email.created_via).to eq(IncomingEmail.created_via_types[:group_smtp]) @@ -109,7 +123,8 @@ RSpec.describe Jobs::GroupSmtpEmail do subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.last.subject).to eq("Re: Help I need support") - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) post_reply_key = PostReplyKey.where(user_id: recipient_user, post_id: post.id).first expect(post_reply_key).to eq(nil) expect(email_log.raw_headers).not_to include("Reply-To: Support Group <#{group.email_username}") @@ -120,7 +135,8 @@ RSpec.describe Jobs::GroupSmtpEmail do subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.last.subject).to eq("Re: Help I need support") - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) expect(email_log).not_to eq(nil) expect(email_log.message_id).to eq("discourse/post/#{post.id}@test.localhost") end @@ -129,7 +145,8 @@ RSpec.describe Jobs::GroupSmtpEmail do subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.last.subject).to eq("Re: Help I need support") - incoming_email = IncomingEmail.find_by(post_id: post.id, topic_id: post.topic_id, user_id: post.user.id) + incoming_email = + IncomingEmail.find_by(post_id: post.id, topic_id: post.topic_id, user_id: post.user.id) expect(incoming_email).not_to eq(nil) expect(incoming_email.message_id).to eq("discourse/post/#{post.id}@test.localhost") expect(incoming_email.created_via).to eq(IncomingEmail.created_via_types[:group_smtp]) @@ -142,7 +159,8 @@ RSpec.describe Jobs::GroupSmtpEmail do subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.last.subject).to eq("Re: Help I need support") - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) post_reply_key = PostReplyKey.where(user_id: recipient_user, post_id: post.id).first expect(post_reply_key).to eq(nil) expect(email_log.raw).not_to include("Reply-To: Support Group <#{group.email_username}") @@ -154,7 +172,8 @@ RSpec.describe Jobs::GroupSmtpEmail do subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.last.subject).to eq("Re: Help I need support") - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) expect(email_log.raw_headers).to include("From: support-group <#{group.email_username}") end @@ -162,7 +181,8 @@ RSpec.describe Jobs::GroupSmtpEmail do subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.last.subject).to eq("Re: Help I need support") - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) expect(email_log.to_address).to eq("test@test.com") expect(email_log.smtp_group_id).to eq(group.id) end @@ -174,7 +194,7 @@ RSpec.describe Jobs::GroupSmtpEmail do expect(ActionMailer::Base.deliveries.count).to eq(1) last_email = ActionMailer::Base.deliveries.last expect(last_email.subject).to eq("Re: Help I need support") - expect(last_email.cc).to match_array(["otherguy@test.com", "cormac@lit.com"]) + expect(last_email.cc).to match_array(%w[otherguy@test.com cormac@lit.com]) end context "when there are cc_addresses" do @@ -183,8 +203,9 @@ RSpec.describe Jobs::GroupSmtpEmail do expect(ActionMailer::Base.deliveries.count).to eq(1) sent_mail = ActionMailer::Base.deliveries.last expect(sent_mail.subject).to eq("Re: Help I need support") - expect(sent_mail.cc).to eq(["otherguy@test.com", "cormac@lit.com"]) - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + expect(sent_mail.cc).to eq(%w[otherguy@test.com cormac@lit.com]) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) expect(email_log.cc_addresses).to eq("otherguy@test.com;cormac@lit.com") expect(email_log.cc_user_ids).to match_array([staged1.id, staged2.id]) end @@ -197,7 +218,8 @@ RSpec.describe Jobs::GroupSmtpEmail do expect(sent_mail.subject).to eq("Re: Help I need support") expect(sent_mail.cc).to eq(["otherguy@test.com"]) expect(sent_mail.bcc).to eq(["cormac@lit.com"]) - email_log = EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) + email_log = + EmailLog.find_by(post_id: post.id, topic_id: post.topic_id, user_id: recipient_user.id) expect(email_log.cc_addresses).to eq("otherguy@test.com") expect(email_log.bcc_addresses).to eq("cormac@lit.com") expect(email_log.cc_user_ids).to match_array([staged1.id]) @@ -208,9 +230,7 @@ RSpec.describe Jobs::GroupSmtpEmail do let(:post_id) { post.topic.posts.first.id } context "when the group has imap enabled" do - before do - group.update!(imap_enabled: true) - end + before { group.update!(imap_enabled: true) } it "aborts and does not send a group SMTP email; the OP is the one that sent the email in the first place" do expect { subject.execute(args) }.not_to(change { EmailLog.count }) @@ -219,9 +239,7 @@ RSpec.describe Jobs::GroupSmtpEmail do end context "when the group does not have imap enabled" do - before do - group.update!(imap_enabled: false) - end + before { group.update!(imap_enabled: false) } it "sends the email as expected" do subject.execute(args) @@ -235,13 +253,15 @@ RSpec.describe Jobs::GroupSmtpEmail do post.trash! subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(0) - expect(SkippedEmailLog.exists?( - email_type: "group_smtp", - user: recipient_user, - post: nil, - to_address: recipient_user.email, - reason_type: SkippedEmailLog.reason_types[:group_smtp_post_deleted] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "group_smtp", + user: recipient_user, + post: nil, + to_address: recipient_user.email, + reason_type: SkippedEmailLog.reason_types[:group_smtp_post_deleted], + ), + ).to eq(true) end end @@ -250,13 +270,15 @@ RSpec.describe Jobs::GroupSmtpEmail do post.topic.trash! subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(0) - expect(SkippedEmailLog.exists?( - email_type: "group_smtp", - user: recipient_user, - post: post, - to_address: recipient_user.email, - reason_type: SkippedEmailLog.reason_types[:group_smtp_topic_deleted] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "group_smtp", + user: recipient_user, + post: post, + to_address: recipient_user.email, + reason_type: SkippedEmailLog.reason_types[:group_smtp_topic_deleted], + ), + ).to eq(true) end end @@ -289,13 +311,15 @@ RSpec.describe Jobs::GroupSmtpEmail do group.update!(smtp_enabled: false) subject.execute(args) expect(ActionMailer::Base.deliveries.count).to eq(0) - expect(SkippedEmailLog.exists?( - email_type: "group_smtp", - user: recipient_user, - post: post, - to_address: recipient_user.email, - reason_type: SkippedEmailLog.reason_types[:group_smtp_disabled_for_group] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "group_smtp", + user: recipient_user, + post: post, + to_address: recipient_user.email, + reason_type: SkippedEmailLog.reason_types[:group_smtp_disabled_for_group], + ), + ).to eq(true) end end end diff --git a/spec/jobs/regular/publish_group_membership_updates_spec.rb b/spec/jobs/regular/publish_group_membership_updates_spec.rb index 822c39e7111..6f5479b090b 100644 --- a/spec/jobs/regular/publish_group_membership_updates_spec.rb +++ b/spec/jobs/regular/publish_group_membership_updates_spec.rb @@ -4,63 +4,54 @@ describe Jobs::PublishGroupMembershipUpdates do fab!(:user) { Fabricate(:user) } fab!(:group) { Fabricate(:group) } - it 'publishes events for added users' do - events = DiscourseEvent.track_events do - subject.execute( - user_ids: [user.id], group_id: group.id, type: 'add' - ) - end + it "publishes events for added users" do + events = + DiscourseEvent.track_events do + subject.execute(user_ids: [user.id], group_id: group.id, type: "add") + end expect(events).to include( event_name: :user_added_to_group, - params: [user, group, { automatic: group.automatic }] + params: [user, group, { automatic: group.automatic }], ) end - it 'publishes events for removed users' do - events = DiscourseEvent.track_events do - subject.execute( - user_ids: [user.id], group_id: group.id, type: 'remove' - ) - end + it "publishes events for removed users" do + events = + DiscourseEvent.track_events do + subject.execute(user_ids: [user.id], group_id: group.id, type: "remove") + end - expect(events).to include( - event_name: :user_removed_from_group, - params: [user, group] - ) + expect(events).to include(event_name: :user_removed_from_group, params: [user, group]) end it "does nothing if the group doesn't exist" do - events = DiscourseEvent.track_events do - subject.execute( - user_ids: [user.id], group_id: nil, type: 'add' - ) - end + events = + DiscourseEvent.track_events do + subject.execute(user_ids: [user.id], group_id: nil, type: "add") + end expect(events).not_to include( event_name: :user_added_to_group, - params: [user, group, { automatic: group.automatic }] + params: [user, group, { automatic: group.automatic }], ) end - it 'fails when the update type is invalid' do - expect { - subject.execute( - user_ids: [user.id], group_id: nil, type: nil - ) - }.to raise_error(Discourse::InvalidParameters) + it "fails when the update type is invalid" do + expect { subject.execute(user_ids: [user.id], group_id: nil, type: nil) }.to raise_error( + Discourse::InvalidParameters, + ) end - it 'does nothing when the user is not human' do - events = DiscourseEvent.track_events do - subject.execute( - user_ids: [Discourse.system_user.id], group_id: nil, type: 'add' - ) - end + it "does nothing when the user is not human" do + events = + DiscourseEvent.track_events do + subject.execute(user_ids: [Discourse.system_user.id], group_id: nil, type: "add") + end expect(events).not_to include( event_name: :user_added_to_group, - params: [user, group, { automatic: group.automatic }] + params: [user, group, { automatic: group.automatic }], ) end end diff --git a/spec/jobs/regular/update_post_uploads_secure_status_spec.rb b/spec/jobs/regular/update_post_uploads_secure_status_spec.rb index 8d728f770d9..ea9fbd2c415 100644 --- a/spec/jobs/regular/update_post_uploads_secure_status_spec.rb +++ b/spec/jobs/regular/update_post_uploads_secure_status_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe Jobs::UpdatePostUploadsSecureStatus do fab!(:post) { Fabricate(:post) } diff --git a/spec/jobs/reindex_search_spec.rb b/spec/jobs/reindex_search_spec.rb index e61f9e71a82..ae63f64d055 100644 --- a/spec/jobs/reindex_search_spec.rb +++ b/spec/jobs/reindex_search_spec.rb @@ -6,13 +6,13 @@ RSpec.describe Jobs::ReindexSearch do Jobs.run_immediately! end - let(:locale) { 'fr' } + let(:locale) { "fr" } # This works since test db has a small record less than limit. # Didn't check `topic` because topic doesn't have posts in fabrication # thus no search data - %w(post category user).each do |m| + %w[post category user].each do |m| it "should rebuild `#{m}` when default_locale changed" do - SiteSetting.default_locale = 'en' + SiteSetting.default_locale = "en" model = Fabricate(m.to_sym) SiteSetting.default_locale = locale subject.execute({}) @@ -27,12 +27,13 @@ RSpec.describe Jobs::ReindexSearch do model.reload subject.execute({}) - expect(model.public_send("#{m}_search_data").version) - .to eq("SearchIndexer::#{m.upcase}_INDEX_VERSION".constantize) + expect(model.public_send("#{m}_search_data").version).to eq( + "SearchIndexer::#{m.upcase}_INDEX_VERSION".constantize, + ) end end - describe 'rebuild_posts' do + describe "rebuild_posts" do class FakeIndexer def self.index(post, force:) get_posts.push(post) @@ -53,9 +54,7 @@ RSpec.describe Jobs::ReindexSearch do end end - after do - FakeIndexer.reset - end + after { FakeIndexer.reset } it "should not reindex posts that belong to a deleted topic or have been trashed" do post = Fabricate(:post) @@ -70,7 +69,7 @@ RSpec.describe Jobs::ReindexSearch do expect(FakeIndexer.posts).to contain_exactly(post) end - it 'should not reindex posts with a developmental version' do + it "should not reindex posts with a developmental version" do post = Fabricate(:post, version: SearchIndexer::MIN_POST_REINDEX_VERSION + 1) subject.rebuild_posts(indexer: FakeIndexer) @@ -78,14 +77,11 @@ RSpec.describe Jobs::ReindexSearch do expect(FakeIndexer.posts).to eq([]) end - it 'should not reindex posts with empty raw' do + it "should not reindex posts with empty raw" do post = Fabricate(:post) post.post_search_data.destroy! - post2 = Fabricate.build(:post, - raw: "", - post_type: Post.types[:small_action] - ) + post2 = Fabricate.build(:post, raw: "", post_type: Post.types[:small_action]) post2.save!(validate: false) @@ -95,7 +91,7 @@ RSpec.describe Jobs::ReindexSearch do end end - describe '#execute' do + describe "#execute" do it "should clean up topic_search_data of trashed topics" do topic = Fabricate(:post).topic topic2 = Fabricate(:post).topic @@ -107,9 +103,7 @@ RSpec.describe Jobs::ReindexSearch do expect { subject.execute({}) }.to change { TopicSearchData.count }.by(-1) expect(Topic.pluck(:id)).to contain_exactly(topic2.id) - expect(TopicSearchData.pluck(:topic_id)).to contain_exactly( - topic2.topic_search_data.topic_id - ) + expect(TopicSearchData.pluck(:topic_id)).to contain_exactly(topic2.topic_search_data.topic_id) end it "should clean up post_search_data of posts with empty raw or posts from trashed topics" do @@ -132,13 +126,9 @@ RSpec.describe Jobs::ReindexSearch do expect { subject.execute({}) }.to change { PostSearchData.count }.by(-3) - expect(Post.pluck(:id)).to contain_exactly( - post.id, post2.id, post3.id, post4.id, post5.id - ) + expect(Post.pluck(:id)).to contain_exactly(post.id, post2.id, post3.id, post4.id, post5.id) - expect(PostSearchData.pluck(:post_id)).to contain_exactly( - post.id, post3.id, post5.id - ) + expect(PostSearchData.pluck(:post_id)).to contain_exactly(post.id, post3.id, post5.id) end end end diff --git a/spec/jobs/remove_banner_spec.rb b/spec/jobs/remove_banner_spec.rb index 6456568eb1a..fecdcfbd097 100644 --- a/spec/jobs/remove_banner_spec.rb +++ b/spec/jobs/remove_banner_spec.rb @@ -4,44 +4,40 @@ RSpec.describe Jobs::RemoveBanner do fab!(:topic) { Fabricate(:topic) } fab!(:user) { topic.user } - context 'when topic is not bannered until' do - it 'doesn’t enqueue a future job to remove it' do - expect do - topic.make_banner!(user) - end.not_to change { Jobs::RemoveBanner.jobs.size } + context "when topic is not bannered until" do + it "doesn’t enqueue a future job to remove it" do + expect do topic.make_banner!(user) end.not_to change { Jobs::RemoveBanner.jobs.size } end end - context 'when topic is bannered until' do - context 'when bannered_until is a valid date' do - it 'enqueues a future job to remove it' do + context "when topic is bannered until" do + context "when bannered_until is a valid date" do + it "enqueues a future job to remove it" do bannered_until = 5.days.from_now expect(topic.archetype).to eq(Archetype.default) - expect do - topic.make_banner!(user, bannered_until.to_s) - end.to change { Jobs::RemoveBanner.jobs.size }.by(1) + expect do topic.make_banner!(user, bannered_until.to_s) end.to change { + Jobs::RemoveBanner.jobs.size + }.by(1) topic.reload expect(topic.archetype).to eq(Archetype.banner) job = Jobs::RemoveBanner.jobs[0] - expect(Time.at(job['at'])).to be_within_one_minute_of(bannered_until) - expect(job['args'][0]['topic_id']).to eq(topic.id) + expect(Time.at(job["at"])).to be_within_one_minute_of(bannered_until) + expect(job["args"][0]["topic_id"]).to eq(topic.id) - job['class'].constantize.new.perform(*job['args']) + job["class"].constantize.new.perform(*job["args"]) topic.reload expect(topic.archetype).to eq(Archetype.default) end end - context 'when bannered_until is an invalid date' do - it 'doesn’t enqueue a future job to remove it' do + context "when bannered_until is an invalid date" do + it "doesn’t enqueue a future job to remove it" do expect do - expect do - topic.make_banner!(user, 'xxx') - end.to raise_error(Discourse::InvalidParameters) + expect do topic.make_banner!(user, "xxx") end.to raise_error(Discourse::InvalidParameters) end.not_to change { Jobs::RemoveBanner.jobs.size } end end diff --git a/spec/jobs/reviewable_priorities_spec.rb b/spec/jobs/reviewable_priorities_spec.rb index 9b3ea9f0306..adad762a6cc 100644 --- a/spec/jobs/reviewable_priorities_spec.rb +++ b/spec/jobs/reviewable_priorities_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true RSpec.describe Jobs::ReviewablePriorities do - it "needs returns 0s with no existing reviewables" do Jobs::ReviewablePriorities.new.execute({}) @@ -38,7 +37,7 @@ RSpec.describe Jobs::ReviewablePriorities do expect(Reviewable.score_required_to_hide_post).to eq(8.33) end - context 'when there are enough reviewables' do + context "when there are enough reviewables" do let(:medium_threshold) { 8.0 } let(:high_threshold) { 13.0 } let(:score_to_hide_post) { 8.66 } @@ -54,7 +53,7 @@ RSpec.describe Jobs::ReviewablePriorities do expect(Reviewable.score_required_to_hide_post).to eq(score_to_hide_post) end - it 'ignore negative scores when calculating priorities' do + it "ignore negative scores when calculating priorities" do create_reviewables(Jobs::ReviewablePriorities.min_reviewables) negative_score = -9 10.times { create_with_score(negative_score) } @@ -67,7 +66,7 @@ RSpec.describe Jobs::ReviewablePriorities do expect(Reviewable.score_required_to_hide_post).to eq(score_to_hide_post) end - it 'ignores non-approved reviewables' do + it "ignores non-approved reviewables" do create_reviewables(Jobs::ReviewablePriorities.min_reviewables) low_score = 2 10.times { create_with_score(low_score, status: :pending) } diff --git a/spec/jobs/send_system_message_spec.rb b/spec/jobs/send_system_message_spec.rb index a21942d01fb..7bacdd5940e 100644 --- a/spec/jobs/send_system_message_spec.rb +++ b/spec/jobs/send_system_message_spec.rb @@ -2,25 +2,37 @@ RSpec.describe Jobs::SendSystemMessage do it "raises an error without a user_id" do - expect { Jobs::SendSystemMessage.new.execute(message_type: 'welcome_invite') }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::SendSystemMessage.new.execute(message_type: "welcome_invite") }.to raise_error( + Discourse::InvalidParameters, + ) end it "raises an error without a message_type" do - expect { Jobs::SendSystemMessage.new.execute(user_id: 1234) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::SendSystemMessage.new.execute(user_id: 1234) }.to raise_error( + Discourse::InvalidParameters, + ) end - context 'with valid parameters' do + context "with valid parameters" do fab!(:user) { Fabricate(:user) } it "should call SystemMessage.create" do - SystemMessage.any_instance.expects(:create).with('welcome_invite', {}) - Jobs::SendSystemMessage.new.execute(user_id: user.id, message_type: 'welcome_invite') + SystemMessage.any_instance.expects(:create).with("welcome_invite", {}) + Jobs::SendSystemMessage.new.execute(user_id: user.id, message_type: "welcome_invite") end it "can send message parameters" do - options = { url: "/t/no-spammers-please/123", edit_delay: 5, flag_reason: "Flagged by community" } - SystemMessage.any_instance.expects(:create).with('post_hidden', options) - Jobs::SendSystemMessage.new.execute(user_id: user.id, message_type: 'post_hidden', message_options: options) + options = { + url: "/t/no-spammers-please/123", + edit_delay: 5, + flag_reason: "Flagged by community", + } + SystemMessage.any_instance.expects(:create).with("post_hidden", options) + Jobs::SendSystemMessage.new.execute( + user_id: user.id, + message_type: "post_hidden", + message_options: options, + ) end end end diff --git a/spec/jobs/suspicious_login_spec.rb b/spec/jobs/suspicious_login_spec.rb index 5d0ac1aa0aa..973aeef20be 100644 --- a/spec/jobs/suspicious_login_spec.rb +++ b/spec/jobs/suspicious_login_spec.rb @@ -1,11 +1,10 @@ # frozen_string_literal: true RSpec.describe Jobs::SuspiciousLogin do - fab!(:user) { Fabricate(:moderator) } let(:zurich) { { latitude: 47.3686498, longitude: 8.5391825 } } # Zurich, Switzerland - let(:bern) { { latitude: 46.947922, longitude: 7.444608 } } # Bern, Switzerland + let(:bern) { { latitude: 46.947922, longitude: 7.444608 } } # Bern, Switzerland let(:london) { { latitude: 51.5073509, longitude: -0.1277583 } } # London, United Kingdom before do @@ -51,8 +50,6 @@ RSpec.describe Jobs::SuspiciousLogin do expect(UserAuthTokenLog.where(action: "suspicious").count).to eq(1) - expect(Jobs::CriticalUserEmail.jobs.first["args"].first["type"]) - .to eq('suspicious_login') + expect(Jobs::CriticalUserEmail.jobs.first["args"].first["type"]).to eq("suspicious_login") end - end diff --git a/spec/jobs/sync_acls_for_uploads_spec.rb b/spec/jobs/sync_acls_for_uploads_spec.rb index a7b43162cad..8f28e27b268 100644 --- a/spec/jobs/sync_acls_for_uploads_spec.rb +++ b/spec/jobs/sync_acls_for_uploads_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe Jobs::SyncAclsForUploads do let(:upload1) { Fabricate(:upload) } @@ -29,7 +29,13 @@ RSpec.describe Jobs::SyncAclsForUploads do end it "handles updates throwing an exception" do - Discourse.store.expects(:update_upload_ACL).raises(StandardError).then.returns(true, true).times(3) + Discourse + .store + .expects(:update_upload_ACL) + .raises(StandardError) + .then + .returns(true, true) + .times(3) Discourse.expects(:warn_exception).once run_job end diff --git a/spec/jobs/tl3_promotions_spec.rb b/spec/jobs/tl3_promotions_spec.rb index 770bb39c166..d1f44556658 100644 --- a/spec/jobs/tl3_promotions_spec.rb +++ b/spec/jobs/tl3_promotions_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Jobs::Tl3Promotions do topics_entered: 1000, posts_read_count: 1000, likes_given: 1000, - likes_received: 1000 + likes_received: 1000, ) end @@ -51,9 +51,7 @@ RSpec.describe Jobs::Tl3Promotions do user end - before do - SiteSetting.tl3_promotion_min_duration = 3 - end + before { SiteSetting.tl3_promotion_min_duration = 3 } it "demotes if was promoted more than X days ago" do user = nil @@ -82,9 +80,7 @@ RSpec.describe Jobs::Tl3Promotions do it "doesn't demote if user hasn't lost requirements (low water mark)" do user = nil - freeze_time(4.days.ago) do - user = create_leader_user - end + freeze_time(4.days.ago) { user = create_leader_user } TrustLevel3Requirements.any_instance.stubs(:requirements_met?).returns(false) TrustLevel3Requirements.any_instance.stubs(:requirements_lost?).returns(false) @@ -103,7 +99,6 @@ RSpec.describe Jobs::Tl3Promotions do TrustLevel3Requirements.any_instance.stubs(:requirements_lost?).returns(true) run_job expect(user.reload.trust_level).to eq(TrustLevel[2]) - end it "doesn't demote user if their group_granted_trust_level is 3" do @@ -120,11 +115,9 @@ RSpec.describe Jobs::Tl3Promotions do end it "doesn't demote with very high tl3_promotion_min_duration value" do - SiteSetting.stubs(:tl3_promotion_min_duration).returns(2000000000) + SiteSetting.stubs(:tl3_promotion_min_duration).returns(2_000_000_000) user = nil - freeze_time(500.days.ago) do - user = create_leader_user - end + freeze_time(500.days.ago) { user = create_leader_user } expect(user).to be_on_tl3_grace_period TrustLevel3Requirements.any_instance.stubs(:requirements_met?).returns(false) TrustLevel3Requirements.any_instance.stubs(:requirements_lost?).returns(true) diff --git a/spec/jobs/toggle_topic_closed_spec.rb b/spec/jobs/toggle_topic_closed_spec.rb index b7c8eacbc4e..406749d8975 100644 --- a/spec/jobs/toggle_topic_closed_spec.rb +++ b/spec/jobs/toggle_topic_closed_spec.rb @@ -3,67 +3,45 @@ RSpec.describe Jobs::ToggleTopicClosed do fab!(:admin) { Fabricate(:admin) } - fab!(:topic) do - Fabricate(:topic_timer, user: admin).topic - end + fab!(:topic) { Fabricate(:topic_timer, user: admin).topic } - it 'should be able to close a topic' do + it "should be able to close a topic" do topic freeze_time(61.minutes.from_now) do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) expect(topic.reload.closed).to eq(true) - expect(Post.last.raw).to eq(I18n.t( - 'topic_statuses.autoclosed_enabled_minutes', count: 61 - )) + expect(Post.last.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_minutes", count: 61)) end end - describe 'opening a topic' do - it 'should be work' do + describe "opening a topic" do + it "should be work" do topic.update!(closed: true) freeze_time(61.minutes.from_now) do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: false - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: false) expect(topic.reload.closed).to eq(false) - expect(Post.last.raw).to eq(I18n.t( - 'topic_statuses.autoclosed_disabled_minutes', count: 61 - )) + expect(Post.last.raw).to eq(I18n.t("topic_statuses.autoclosed_disabled_minutes", count: 61)) end end - describe 'when category has auto close configured' do + describe "when category has auto close configured" do fab!(:category) do - Fabricate(:category, - auto_close_based_on_last_post: true, - auto_close_hours: 5 - ) + Fabricate(:category, auto_close_based_on_last_post: true, auto_close_hours: 5) end fab!(:topic) { Fabricate(:topic, category: category, closed: true) } it "should restore the category's auto close timer" do - Fabricate(:topic_timer, - status_type: TopicTimer.types[:open], - topic: topic, - user: admin - ) + Fabricate(:topic_timer, status_type: TopicTimer.types[:open], topic: topic, user: admin) freeze_time(61.minutes.from_now) do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: false - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: false) expect(topic.reload.closed).to eq(false) @@ -76,61 +54,47 @@ RSpec.describe Jobs::ToggleTopicClosed do end end - describe 'when trying to close a topic that has already been closed' do - it 'should delete the topic timer' do + describe "when trying to close a topic that has already been closed" do + it "should delete the topic timer" do freeze_time(topic.public_topic_timer.execute_at + 1.minute) topic.update!(closed: true) expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false) end end - describe 'when trying to close a topic that has been deleted' do - it 'should delete the topic timer' do + describe "when trying to close a topic that has been deleted" do + it "should delete the topic timer" do freeze_time(topic.public_topic_timer.execute_at + 1.minute) topic.trash! expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false) end end - describe 'when user is no longer authorized to close topics' do + describe "when user is no longer authorized to close topics" do fab!(:user) { Fabricate(:user) } - fab!(:topic) do - Fabricate(:topic_timer, user: user).topic - end + fab!(:topic) { Fabricate(:topic_timer, user: user).topic } - it 'should destroy the topic timer' do + it "should destroy the topic timer" do freeze_time(topic.public_topic_timer.execute_at + 1.minute) expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false) expect(topic.reload.closed).to eq(false) end it "should reconfigure topic timer if category's topics are set to autoclose" do - category = Fabricate(:category, - auto_close_based_on_last_post: true, - auto_close_hours: 5 - ) + category = Fabricate(:category, auto_close_based_on_last_post: true, auto_close_hours: 5) topic = Fabricate(:topic, category: category) topic.public_topic_timer.update!(user: user) @@ -138,12 +102,10 @@ RSpec.describe Jobs::ToggleTopicClosed do freeze_time(topic.public_topic_timer.execute_at + 1.minute) expect do - described_class.new.execute( - topic_timer_id: topic.public_topic_timer.id, - state: true - ) - end.to change { topic.reload.public_topic_timer.user }.from(user).to(Discourse.system_user) - .and change { topic.public_topic_timer.id } + described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true) + end.to change { topic.reload.public_topic_timer.user }.from(user).to( + Discourse.system_user, + ).and change { topic.public_topic_timer.id } expect(topic.reload.closed).to eq(false) end diff --git a/spec/jobs/topic_timer_enqueuer_spec.rb b/spec/jobs/topic_timer_enqueuer_spec.rb index 1500cb4f4f1..95f73fd93ef 100644 --- a/spec/jobs/topic_timer_enqueuer_spec.rb +++ b/spec/jobs/topic_timer_enqueuer_spec.rb @@ -4,21 +4,39 @@ RSpec.describe Jobs::TopicTimerEnqueuer do subject { described_class.new } fab!(:timer1) do - Fabricate(:topic_timer, execute_at: 1.minute.ago, created_at: 1.hour.ago, status_type: TopicTimer.types[:close]) + Fabricate( + :topic_timer, + execute_at: 1.minute.ago, + created_at: 1.hour.ago, + status_type: TopicTimer.types[:close], + ) end fab!(:timer2) do - Fabricate(:topic_timer, execute_at: 1.minute.ago, created_at: 1.hour.ago, status_type: TopicTimer.types[:open]) + Fabricate( + :topic_timer, + execute_at: 1.minute.ago, + created_at: 1.hour.ago, + status_type: TopicTimer.types[:open], + ) end fab!(:future_timer) do - Fabricate(:topic_timer, execute_at: 1.hours.from_now, created_at: 1.hour.ago, status_type: TopicTimer.types[:close]) + Fabricate( + :topic_timer, + execute_at: 1.hours.from_now, + created_at: 1.hour.ago, + status_type: TopicTimer.types[:close], + ) end fab!(:deleted_timer) do - Fabricate(:topic_timer, execute_at: 1.minute.ago, created_at: 1.hour.ago, status_type: TopicTimer.types[:close]) + Fabricate( + :topic_timer, + execute_at: 1.minute.ago, + created_at: 1.hour.ago, + status_type: TopicTimer.types[:close], + ) end - before do - deleted_timer.trash! - end + before { deleted_timer.trash! } it "does not enqueue deleted timers" do expect_not_enqueued_with(job: :close_topic, args: { topic_timer_id: deleted_timer.id }) diff --git a/spec/jobs/truncate_user_flag_stats_spec.rb b/spec/jobs/truncate_user_flag_stats_spec.rb index 3c64369ff20..ffd1c0981e9 100644 --- a/spec/jobs/truncate_user_flag_stats_spec.rb +++ b/spec/jobs/truncate_user_flag_stats_spec.rb @@ -15,9 +15,7 @@ RSpec.describe Jobs::TruncateUserFlagStats do end it "raises an error without user ids" do - expect { - described_class.new.execute({}) - }.to raise_error(Discourse::InvalidParameters) + expect { described_class.new.execute({}) }.to raise_error(Discourse::InvalidParameters) end it "does nothing if the user doesn't have enough flags" do @@ -79,5 +77,4 @@ RSpec.describe Jobs::TruncateUserFlagStats do expect(other_user.user_stat.flags_disagreed).to eq(1) expect(other_user.user_stat.flags_ignored).to eq(1) end - end diff --git a/spec/jobs/update_gravatar_spec.rb b/spec/jobs/update_gravatar_spec.rb index 4b72fe95275..24637edb01a 100644 --- a/spec/jobs/update_gravatar_spec.rb +++ b/spec/jobs/update_gravatar_spec.rb @@ -2,14 +2,18 @@ RSpec.describe Jobs::UpdateGravatar do fab!(:user) { Fabricate(:user) } - let(:temp) { Tempfile.new('test') } + let(:temp) { Tempfile.new("test") } fab!(:upload) { Fabricate(:upload, user: user) } let(:avatar) { user.create_user_avatar! } it "picks gravatar if system avatar is picked and gravatar was just downloaded" do temp.binmode # tiny valid png - temp.write(Base64.decode64("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==")) + temp.write( + Base64.decode64( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==", + ), + ) temp.rewind FileHelper.expects(:download).returns(temp) @@ -38,8 +42,7 @@ RSpec.describe Jobs::UpdateGravatar do SiteSetting.automatically_download_gravatars = true - expect { user.refresh_avatar } - .not_to change { Jobs::UpdateGravatar.jobs.count } + expect { user.refresh_avatar }.not_to change { Jobs::UpdateGravatar.jobs.count } user.reload expect(user.uploaded_avatar_id).to eq(nil) diff --git a/spec/jobs/update_s3_inventory_spec.rb b/spec/jobs/update_s3_inventory_spec.rb index ff8e032f040..ab43aa4060b 100644 --- a/spec/jobs/update_s3_inventory_spec.rb +++ b/spec/jobs/update_s3_inventory_spec.rb @@ -20,7 +20,8 @@ RSpec.describe Jobs::UpdateS3Inventory do @client.expects(:put_bucket_policy).with( bucket: "special-bucket", - policy: %Q|{"Version":"2012-10-17","Statement":[{"Sid":"InventoryAndAnalyticsPolicy","Effect":"Allow","Principal":{"Service":"s3.amazonaws.com"},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::special-bucket/#{path}/*"],"Condition":{"ArnLike":{"aws:SourceArn":"arn:aws:s3:::special-bucket"},"StringEquals":{"s3:x-amz-acl":"bucket-owner-full-control"}}}]}| + policy: + %Q|{"Version":"2012-10-17","Statement":[{"Sid":"InventoryAndAnalyticsPolicy","Effect":"Allow","Principal":{"Service":"s3.amazonaws.com"},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::special-bucket/#{path}/*"],"Condition":{"ArnLike":{"aws:SourceArn":"arn:aws:s3:::special-bucket"},"StringEquals":{"s3:x-amz-acl":"bucket-owner-full-control"}}}]}|, ) @client.expects(:put_bucket_inventory_configuration) @client.expects(:put_bucket_inventory_configuration).with( @@ -31,19 +32,21 @@ RSpec.describe Jobs::UpdateS3Inventory do s3_bucket_destination: { bucket: "arn:aws:s3:::special-bucket", prefix: path, - format: "CSV" - } + format: "CSV", + }, }, filter: { - prefix: id + prefix: id, }, is_enabled: true, id: id, included_object_versions: "Current", optional_fields: ["ETag"], - schedule: { frequency: "Daily" } + schedule: { + frequency: "Daily", + }, }, - use_accelerate_endpoint: false + use_accelerate_endpoint: false, ) described_class.new.execute(nil) diff --git a/spec/jobs/update_username_spec.rb b/spec/jobs/update_username_spec.rb index fd4a57a92bc..f95699a0776 100644 --- a/spec/jobs/update_username_spec.rb +++ b/spec/jobs/update_username_spec.rb @@ -3,15 +3,16 @@ RSpec.describe Jobs::UpdateUsername do fab!(:user) { Fabricate(:user) } - it 'does not do anything if user_id is invalid' do - events = DiscourseEvent.track_events do - described_class.new.execute( - user_id: -999, - old_username: user.username, - new_username: 'somenewusername', - avatar_template: user.avatar_template - ) - end + it "does not do anything if user_id is invalid" do + events = + DiscourseEvent.track_events do + described_class.new.execute( + user_id: -999, + old_username: user.username, + new_username: "somenewusername", + avatar_template: user.avatar_template, + ) + end expect(events).to eq([]) end diff --git a/spec/jobs/user_email_spec.rb b/spec/jobs/user_email_spec.rb index efda1132b6f..3a52f547be4 100644 --- a/spec/jobs/user_email_spec.rb +++ b/spec/jobs/user_email_spec.rb @@ -1,33 +1,44 @@ # frozen_string_literal: true RSpec.describe Jobs::UserEmail do - before do - SiteSetting.email_time_window_mins = 10 - end + before { SiteSetting.email_time_window_mins = 10 } fab!(:user) { Fabricate(:user, last_seen_at: 11.minutes.ago) } fab!(:staged) { Fabricate(:user, staged: true, last_seen_at: 11.minutes.ago) } - fab!(:suspended) { Fabricate(:user, last_seen_at: 10.minutes.ago, suspended_at: 5.minutes.ago, suspended_till: 7.days.from_now) } + fab!(:suspended) do + Fabricate( + :user, + last_seen_at: 10.minutes.ago, + suspended_at: 5.minutes.ago, + suspended_till: 7.days.from_now, + ) + end fab!(:anonymous) { Fabricate(:anonymous, last_seen_at: 11.minutes.ago) } it "raises an error when there is no user" do - expect { Jobs::UserEmail.new.execute(type: :digest) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::UserEmail.new.execute(type: :digest) }.to raise_error( + Discourse::InvalidParameters, + ) end it "raises an error when there is no type" do - expect { Jobs::UserEmail.new.execute(user_id: user.id) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::UserEmail.new.execute(user_id: user.id) }.to raise_error( + Discourse::InvalidParameters, + ) end it "raises an error when the type doesn't exist" do - expect { Jobs::UserEmail.new.execute(type: :no_method, user_id: user.id) }.to raise_error(Discourse::InvalidParameters) + expect { Jobs::UserEmail.new.execute(type: :no_method, user_id: user.id) }.to raise_error( + Discourse::InvalidParameters, + ) end - context 'when digest can be generated' do + context "when digest can be generated" do fab!(:user) { Fabricate(:user, last_seen_at: 8.days.ago, last_emailed_at: 8.days.ago) } fab!(:popular_topic) { Fabricate(:topic, user: Fabricate(:admin), created_at: 1.hour.ago) } it "doesn't call the mailer when the user is missing" do - Jobs::UserEmail.new.execute(type: :digest, user_id: User.last.id + 10000) + Jobs::UserEmail.new.execute(type: :digest, user_id: User.last.id + 10_000) expect(ActionMailer::Base.deliveries).to eq([]) end @@ -37,7 +48,7 @@ RSpec.describe Jobs::UserEmail do expect(ActionMailer::Base.deliveries).to eq([]) end - context 'when not emailed recently' do + context "when not emailed recently" do before do freeze_time user.update!(last_emailed_at: 8.days.ago) @@ -50,14 +61,14 @@ RSpec.describe Jobs::UserEmail do end end - context 'when recently emailed' do + context "when recently emailed" do before do freeze_time user.update!(last_emailed_at: 2.hours.ago) user.user_option.update!(digest_after_minutes: 1.day.to_i / 60) end - it 'skips sending digest email' do + it "skips sending digest email" do Jobs::UserEmail.new.execute(type: :digest, user_id: user.id) expect(ActionMailer::Base.deliveries).to eq([]) expect(user.user_stat.reload.digest_attempted_at).to eq_time(Time.zone.now) @@ -70,39 +81,41 @@ RSpec.describe Jobs::UserEmail do email_token = Fabricate(:email_token) user.user_stat.update(bounce_score: SiteSetting.bounce_score_threshold + 1) - Jobs::CriticalUserEmail.new.execute(type: "signup", user_id: user.id, email_token: email_token.token) + Jobs::CriticalUserEmail.new.execute( + type: "signup", + user_id: user.id, + email_token: email_token.token, + ) email_log = EmailLog.where(user_id: user.id).last expect(email_log.email_type).to eq("signup") - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - user.email - ) + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) end end - context 'with to_address' do - it 'overwrites a to_address when present' do - Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id, to_address: 'jake@adventuretime.ooo') - - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - 'jake@adventuretime.ooo' + context "with to_address" do + it "overwrites a to_address when present" do + Jobs::UserEmail.new.execute( + type: :confirm_new_email, + user_id: user.id, + to_address: "jake@adventuretime.ooo", ) + + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly("jake@adventuretime.ooo") end end context "with disable_emails setting" do it "sends when no" do - SiteSetting.disable_emails = 'no' + SiteSetting.disable_emails = "no" Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - user.email - ) + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) end it "does not send an email when yes" do - SiteSetting.disable_emails = 'yes' + SiteSetting.disable_emails = "yes" Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id) expect(ActionMailer::Base.deliveries).to eq([]) @@ -111,17 +124,16 @@ RSpec.describe Jobs::UserEmail do context "when recently seen" do fab!(:post) { Fabricate(:post, user: user) } - fab!(:notification) { Fabricate( + fab!(:notification) do + Fabricate( :notification, user: user, topic: post.topic, post_number: post.post_number, - data: { original_post_id: post.id }.to_json + data: { original_post_id: post.id }.to_json, ) - } - before do - user.update_column(:last_seen_at, 9.minutes.ago) end + before { user.update_column(:last_seen_at, 9.minutes.ago) } it "doesn't send an email to a user that's been recently seen" do Jobs::UserEmail.new.execute(type: :user_replied, user_id: user.id, post_id: post.id) @@ -130,30 +142,38 @@ RSpec.describe Jobs::UserEmail do it "does send an email to a user that's been recently seen but has email_level set to always" do user.user_option.update(email_level: UserOption.email_level_types[:always]) - PostTiming.create!(topic_id: post.topic_id, post_number: post.post_number, user_id: user.id, msecs: 100) - - Jobs::UserEmail.new.execute( - type: :user_replied, + PostTiming.create!( + topic_id: post.topic_id, + post_number: post.post_number, user_id: user.id, - post_id: post.id, - notification_id: notification.id + msecs: 100, ) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - user.email - ) - end - - it "doesn't send an email even if email_level is set to always if `force_respect_seen_recently` arg is true" do - user.user_option.update(email_level: UserOption.email_level_types[:always]) - PostTiming.create!(topic_id: post.topic_id, post_number: post.post_number, user_id: user.id, msecs: 100) - Jobs::UserEmail.new.execute( type: :user_replied, user_id: user.id, post_id: post.id, notification_id: notification.id, - force_respect_seen_recently: true + ) + + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) + end + + it "doesn't send an email even if email_level is set to always if `force_respect_seen_recently` arg is true" do + user.user_option.update(email_level: UserOption.email_level_types[:always]) + PostTiming.create!( + topic_id: post.topic_id, + post_number: post.post_number, + user_id: user.id, + msecs: 100, + ) + + Jobs::UserEmail.new.execute( + type: :user_replied, + user_id: user.id, + post_id: post.id, + notification_id: notification.id, + force_respect_seen_recently: true, ) expect(ActionMailer::Base.deliveries).to eq([]) end @@ -170,7 +190,7 @@ RSpec.describe Jobs::UserEmail do type: :user_private_message, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) email = ActionMailer::Base.deliveries.first @@ -178,7 +198,7 @@ RSpec.describe Jobs::UserEmail do expect(email.to).to contain_exactly(user.email) html_part = email.parts.find { |x| x.content_type.include? "html" } - expect(html_part.body.to_s).to_not include('%{email_content}') + expect(html_part.body.to_s).to_not include("%{email_content}") expect(html_part.body.to_s).to include('\0') end @@ -196,7 +216,7 @@ RSpec.describe Jobs::UserEmail do type: :user_private_message, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) email = ActionMailer::Base.deliveries.first @@ -218,12 +238,10 @@ RSpec.describe Jobs::UserEmail do type: :user_private_message, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - user.email - ) + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) end it "doesn't send a PM email to a user that's been recently seen and has email_messages_level set to never" do @@ -246,9 +264,7 @@ RSpec.describe Jobs::UserEmail do context "with email_log" do fab!(:post) { Fabricate(:post, created_at: 30.seconds.ago) } - before do - SiteSetting.editing_grace_period = 0 - end + before { SiteSetting.editing_grace_period = 0 } it "creates an email log when the mail is sent (via Email::Sender)" do freeze_time @@ -257,9 +273,9 @@ RSpec.describe Jobs::UserEmail do user.update!(last_emailed_at: last_emailed_at) Topic.last.update(created_at: 1.minute.ago) - expect do - Jobs::UserEmail.new.execute(type: :digest, user_id: user.id) - end.to change { EmailLog.count }.by(1) + expect do Jobs::UserEmail.new.execute(type: :digest, user_id: user.id) end.to change { + EmailLog.count + }.by(1) email_log = EmailLog.last @@ -273,22 +289,21 @@ RSpec.describe Jobs::UserEmail do freeze_time last_emailed_at = 7.days.ago - user.update!( - last_emailed_at: last_emailed_at, - suspended_till: 1.year.from_now - ) + user.update!(last_emailed_at: last_emailed_at, suspended_till: 1.year.from_now) - expect do - Jobs::UserEmail.new.execute(type: :digest, user_id: user.id) - end.to change { SkippedEmailLog.count }.by(1) + expect do Jobs::UserEmail.new.execute(type: :digest, user_id: user.id) end.to change { + SkippedEmailLog.count + }.by(1) - expect(SkippedEmailLog.exists?( - email_type: "digest", - user: user, - post: nil, - to_address: user.email, - reason_type: SkippedEmailLog.reason_types[:user_email_user_suspended_not_pm] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "digest", + user: user, + post: nil, + to_address: user.email, + reason_type: SkippedEmailLog.reason_types[:user_email_user_suspended_not_pm], + ), + ).to eq(true) # last_emailed_at doesn't change expect(user.last_emailed_at).to eq_time(last_emailed_at) @@ -302,21 +317,23 @@ RSpec.describe Jobs::UserEmail do Jobs::UserEmail.new.execute(type: :user_posted, user_id: user.id, post_id: post.id) end.to change { SkippedEmailLog.count }.by(1) - expect(SkippedEmailLog.exists?( - email_type: "user_posted", - user: user, - post: post, - to_address: user.email, - reason_type: SkippedEmailLog.reason_types[:user_email_access_denied] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "user_posted", + user: user, + post: post, + to_address: user.email, + reason_type: SkippedEmailLog.reason_types[:user_email_access_denied], + ), + ).to eq(true) expect(ActionMailer::Base.deliveries).to eq([]) end end - context 'with args' do - it 'passes a token as an argument when a token is present' do - Jobs::UserEmail.new.execute(type: :forgot_password, user_id: user.id, email_token: 'asdfasdf') + context "with args" do + it "passes a token as an argument when a token is present" do + Jobs::UserEmail.new.execute(type: :forgot_password, user_id: user.id, email_token: "asdfasdf") mail = ActionMailer::Base.deliveries.first @@ -332,14 +349,18 @@ RSpec.describe Jobs::UserEmail do requested_by: requested_by, new_email_token: email_token, new_email: "testnew@test.com", - change_state: EmailChangeRequest.states[:authorizing_new] + change_state: EmailChangeRequest.states[:authorizing_new], ) end context "when the change was requested by admin" do let(:requested_by) { Fabricate(:admin) } it "passes along true for the requested_by_admin param which changes the wording in the email" do - Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id, email_token: email_token.token) + Jobs::UserEmail.new.execute( + type: :confirm_new_email, + user_id: user.id, + email_token: email_token.token, + ) mail = ActionMailer::Base.deliveries.first expect(mail.body).to include("This email change was requested by a site admin.") end @@ -348,7 +369,11 @@ RSpec.describe Jobs::UserEmail do context "when the change was requested by the user" do let(:requested_by) { user } it "passes along false for the requested_by_admin param which changes the wording in the email" do - Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id, email_token: email_token.token) + Jobs::UserEmail.new.execute( + type: :confirm_new_email, + user_id: user.id, + email_token: email_token.token, + ) mail = ActionMailer::Base.deliveries.first expect(mail.body).not_to include("This email change was requested by a site admin.") end @@ -357,7 +382,11 @@ RSpec.describe Jobs::UserEmail do context "when requested_by record is not present" do let(:requested_by) { nil } it "passes along false for the requested_by_admin param which changes the wording in the email" do - Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id, email_token: email_token.token) + Jobs::UserEmail.new.execute( + type: :confirm_new_email, + user_id: user.id, + email_token: email_token.token, + ) mail = ActionMailer::Base.deliveries.first expect(mail.body).not_to include("This email change was requested by a site admin.") end @@ -368,7 +397,12 @@ RSpec.describe Jobs::UserEmail do fab!(:post) { Fabricate(:post, user: user) } it "doesn't send the email if you've seen the post" do - PostTiming.record_timing(topic_id: post.topic_id, user_id: user.id, post_number: post.post_number, msecs: 6666) + PostTiming.record_timing( + topic_id: post.topic_id, + user_id: user.id, + post_number: post.post_number, + msecs: 6666, + ) Jobs::UserEmail.new.execute(type: :user_private_message, user_id: user.id, post_id: post.id) expect(ActionMailer::Base.deliveries).to eq([]) @@ -388,9 +422,13 @@ RSpec.describe Jobs::UserEmail do expect(ActionMailer::Base.deliveries).to eq([]) end - context 'when user is suspended' do + context "when user is suspended" do it "doesn't send email for a pm from a regular user" do - Jobs::UserEmail.new.execute(type: :user_private_message, user_id: suspended.id, post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_private_message, + user_id: suspended.id, + post_id: post.id, + ) expect(ActionMailer::Base.deliveries).to eq([]) end @@ -399,51 +437,57 @@ RSpec.describe Jobs::UserEmail do pm_from_staff = Fabricate(:post, user: Fabricate(:moderator)) pm_from_staff.topic.topic_allowed_users.create!(user_id: suspended.id) - pm_notification = Fabricate(:notification, - user: suspended, - topic: pm_from_staff.topic, - post_number: pm_from_staff.post_number, - data: { original_post_id: pm_from_staff.id }.to_json - ) + pm_notification = + Fabricate( + :notification, + user: suspended, + topic: pm_from_staff.topic, + post_number: pm_from_staff.post_number, + data: { original_post_id: pm_from_staff.id }.to_json, + ) Jobs::UserEmail.new.execute( type: :user_private_message, user_id: suspended.id, post_id: pm_from_staff.id, - notification_id: pm_notification.id + notification_id: pm_notification.id, ) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - suspended.email - ) + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(suspended.email) end it "doesn't send PM from system user" do pm_from_system = SystemMessage.create(suspended, :unsilenced) - system_pm_notification = Fabricate(:notification, - user: suspended, - topic: pm_from_system.topic, - post_number: pm_from_system.post_number, - data: { original_post_id: pm_from_system.id }.to_json - ) + system_pm_notification = + Fabricate( + :notification, + user: suspended, + topic: pm_from_system.topic, + post_number: pm_from_system.post_number, + data: { original_post_id: pm_from_system.id }.to_json, + ) Jobs::UserEmail.new.execute( type: :user_private_message, user_id: suspended.id, post_id: pm_from_system.id, - notification_id: system_pm_notification.id + notification_id: system_pm_notification.id, ) expect(ActionMailer::Base.deliveries).to eq([]) end end - context 'when user is anonymous' do + context "when user is anonymous" do before { SiteSetting.allow_anonymous_posting = true } it "doesn't send email for a pm from a regular user" do - Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_private_message, + user_id: anonymous.id, + post_id: post.id, + ) expect(ActionMailer::Base.deliveries).to eq([]) end @@ -451,46 +495,52 @@ RSpec.describe Jobs::UserEmail do it "doesn't send email for a pm from a staff user" do pm_from_staff = Fabricate(:post, user: Fabricate(:moderator)) pm_from_staff.topic.topic_allowed_users.create!(user_id: anonymous.id) - Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, post_id: pm_from_staff.id) + Jobs::UserEmail.new.execute( + type: :user_private_message, + user_id: anonymous.id, + post_id: pm_from_staff.id, + ) expect(ActionMailer::Base.deliveries).to eq([]) end end end - context 'with notification' do + context "with notification" do fab!(:post) { Fabricate(:post, user: user) } - fab!(:notification) { - Fabricate(:notification, - user: user, - topic: post.topic, - post_number: post.post_number, - data: { - original_post_id: post.id - }.to_json - ) - } + fab!(:notification) do + Fabricate( + :notification, + user: user, + topic: post.topic, + post_number: post.post_number, + data: { original_post_id: post.id }.to_json, + ) + end it "doesn't send the email if the notification has been seen" do notification.update_column(:read, true) - message, err = Jobs::UserEmail.new.message_for_email( - user, - post, - :user_mentioned, - notification, - notification_type: notification.notification_type, - notification_data_hash: notification.data_hash - ) + message, err = + Jobs::UserEmail.new.message_for_email( + user, + post, + :user_mentioned, + notification, + notification_type: notification.notification_type, + notification_data_hash: notification.data_hash, + ) expect(message).to eq(nil) - expect(SkippedEmailLog.exists?( - email_type: "user_mentioned", - user: user, - post: post, - to_address: user.email, - reason_type: SkippedEmailLog.reason_types[:user_email_notification_already_read] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "user_mentioned", + user: user, + post: post, + to_address: user.email, + reason_type: SkippedEmailLog.reason_types[:user_email_notification_already_read], + ), + ).to eq(true) end it "does send the email if the notification has been seen but user has email_level set to always" do @@ -501,12 +551,10 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - user.email - ) + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) end it "does send the email if the user is using daily mailing list mode" do @@ -516,12 +564,10 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - user.email - ) + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) end it "sends the mail if the user enabled mailing list mode, but mailing list mode is disabled globally" do @@ -531,7 +577,7 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) @@ -545,7 +591,7 @@ RSpec.describe Jobs::UserEmail do type: :user_replied, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) expect(ActionMailer::Base.deliveries).to eq([]) @@ -559,19 +605,17 @@ RSpec.describe Jobs::UserEmail do type: :user_replied, user_id: user.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) - expect(ActionMailer::Base.deliveries.first.to).to contain_exactly( - user.email - ) + expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(user.email) end end - context 'when max_emails_per_day_per_user limit is reached' do + context "when max_emails_per_day_per_user limit is reached" do before do SiteSetting.max_emails_per_day_per_user = 2 - 2.times { Fabricate(:email_log, user: user, email_type: 'blah', to_address: user.email) } + 2.times { Fabricate(:email_log, user: user, email_type: "blah", to_address: user.email) } end it "does not send notification if limit is reached" do @@ -581,18 +625,20 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, notification_id: notification.id, - post_id: post.id + post_id: post.id, ) end end.to change { SkippedEmailLog.count }.by(1) - expect(SkippedEmailLog.exists?( - email_type: "user_mentioned", - user: user, - post: post, - to_address: user.email, - reason_type: SkippedEmailLog.reason_types[:exceeded_emails_limit] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "user_mentioned", + user: user, + post: post, + to_address: user.email, + reason_type: SkippedEmailLog.reason_types[:exceeded_emails_limit], + ), + ).to eq(true) freeze_time(Time.zone.now.tomorrow + 1.second) @@ -601,7 +647,7 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, notification_id: notification.id, - post_id: post.id + post_id: post.id, ) end.not_to change { SkippedEmailLog.count } end @@ -615,10 +661,7 @@ RSpec.describe Jobs::UserEmail do ) end.to change { EmailLog.count }.by(1) - expect(EmailLog.exists?( - email_type: "forgot_password", - user: user, - )).to eq(true) + expect(EmailLog.exists?(email_type: "forgot_password", user: user)).to eq(true) end end @@ -631,7 +674,7 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, notification_id: notification.id, - post_id: post.id + post_id: post.id, ) user.user_stat.reload @@ -643,7 +686,7 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, notification_id: notification.id, - post_id: post.id + post_id: post.id, ) user.user_stat.reload @@ -658,17 +701,19 @@ RSpec.describe Jobs::UserEmail do type: :user_mentioned, user_id: user.id, notification_id: notification.id, - post_id: post.id + post_id: post.id, ) end.to change { SkippedEmailLog.count }.by(1) - expect(SkippedEmailLog.exists?( - email_type: "user_mentioned", - user: user, - post: post, - to_address: user.email, - reason_type: SkippedEmailLog.reason_types[:exceeded_bounces_limit] - )).to eq(true) + expect( + SkippedEmailLog.exists?( + email_type: "user_mentioned", + user: user, + post: post, + to_address: user.email, + reason_type: SkippedEmailLog.reason_types[:exceeded_bounces_limit], + ), + ).to eq(true) end it "doesn't send the mail if the user is using individual mailing list mode" do @@ -676,15 +721,34 @@ RSpec.describe Jobs::UserEmail do user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 1) # sometimes, we pass the notification_id - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_id: notification.id, + post_id: post.id, + ) # other times, we only pass the type of notification - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_type: "posted", + post_id: post.id, + ) # When post is nil - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted") + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_type: "posted", + ) # When post does not have a topic post = Fabricate(:post) post.topic.destroy - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_type: "posted", + post_id: post.id, + ) expect(ActionMailer::Base.deliveries).to eq([]) end @@ -694,57 +758,84 @@ RSpec.describe Jobs::UserEmail do user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 2) # sometimes, we pass the notification_id - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_id: notification.id, + post_id: post.id, + ) # other times, we only pass the type of notification - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_type: "posted", + post_id: post.id, + ) # When post is nil - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted") + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_type: "posted", + ) # When post does not have a topic post = Fabricate(:post) post.topic.destroy - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_type: "posted", + post_id: post.id, + ) expect(ActionMailer::Base.deliveries).to eq([]) end it "doesn't send the email if the post has been user deleted" do post.update_column(:user_deleted, true) - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) + Jobs::UserEmail.new.execute( + type: :user_mentioned, + user_id: user.id, + notification_id: notification.id, + post_id: post.id, + ) expect(ActionMailer::Base.deliveries).to eq([]) end - context 'when user is suspended' do + context "when user is suspended" do it "doesn't send email for a pm from a regular user" do - msg, err = Jobs::UserEmail.new.message_for_email( + msg, err = + Jobs::UserEmail.new.message_for_email( suspended, Fabricate.build(:post), :user_private_message, - notification - ) + notification, + ) expect(msg).to eq(nil) expect(err).not_to eq(nil) end - context 'with pm from staff' do + context "with pm from staff" do before do @pm_from_staff = Fabricate(:post, user: Fabricate(:moderator)) @pm_from_staff.topic.topic_allowed_users.create!(user_id: suspended.id) - @pm_notification = Fabricate(:notification, - user: suspended, - topic: @pm_from_staff.topic, - post_number: @pm_from_staff.post_number, - data: { original_post_id: @pm_from_staff.id }.to_json - ) + @pm_notification = + Fabricate( + :notification, + user: suspended, + topic: @pm_from_staff.topic, + post_number: @pm_from_staff.post_number, + data: { original_post_id: @pm_from_staff.id }.to_json, + ) end let :sent_message do Jobs::UserEmail.new.message_for_email( - suspended, - @pm_from_staff, - :user_private_message, - @pm_notification + suspended, + @pm_from_staff, + :user_private_message, + @pm_notification, ) end @@ -764,7 +855,7 @@ RSpec.describe Jobs::UserEmail do end end - context 'when user is anonymous' do + context "when user is anonymous" do before { SiteSetting.allow_anonymous_posting = true } it "doesn't send email for a pm from a regular user" do @@ -772,7 +863,7 @@ RSpec.describe Jobs::UserEmail do type: :user_private_message, user_id: anonymous.id, post_id: post.id, - notification_id: notification.id + notification_id: notification.id, ) expect(ActionMailer::Base.deliveries).to eq([]) @@ -781,17 +872,19 @@ RSpec.describe Jobs::UserEmail do it "doesn't send email for a pm from staff" do pm_from_staff = Fabricate(:post, user: Fabricate(:moderator)) pm_from_staff.topic.topic_allowed_users.create!(user_id: anonymous.id) - pm_notification = Fabricate(:notification, - user: anonymous, - topic: pm_from_staff.topic, - post_number: pm_from_staff.post_number, - data: { original_post_id: pm_from_staff.id }.to_json - ) + pm_notification = + Fabricate( + :notification, + user: anonymous, + topic: pm_from_staff.topic, + post_number: pm_from_staff.post_number, + data: { original_post_id: pm_from_staff.id }.to_json, + ) Jobs::UserEmail.new.execute( type: :user_private_message, user_id: anonymous.id, post_id: pm_from_staff.id, - notification_id: pm_notification.id + notification_id: pm_notification.id, ) expect(ActionMailer::Base.deliveries).to eq([]) diff --git a/spec/lib/admin_confirmation_spec.rb b/spec/lib/admin_confirmation_spec.rb index 10e065c6036..e9d345939cc 100644 --- a/spec/lib/admin_confirmation_spec.rb +++ b/spec/lib/admin_confirmation_spec.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true -require 'admin_confirmation' +require "admin_confirmation" RSpec.describe AdminConfirmation do - fab!(:admin) { Fabricate(:admin) } fab!(:user) { Fabricate(:user) } @@ -32,25 +31,27 @@ RSpec.describe AdminConfirmation do expect(ac.target_user).to eq(user) expect(ac.token).to eq(@token) - expect_enqueued_with(job: :send_system_message, args: { user_id: user.id, message_type: 'welcome_staff', message_options: { role: :admin } }) do - ac.email_confirmed! - end + expect_enqueued_with( + job: :send_system_message, + args: { + user_id: user.id, + message_type: "welcome_staff", + message_options: { + role: :admin, + }, + }, + ) { ac.email_confirmed! } user.reload expect(user.admin?).to eq(true) # It creates a staff log - logs = UserHistory.where( - action: UserHistory.actions[:grant_admin], - target_user_id: user.id - ) + logs = UserHistory.where(action: UserHistory.actions[:grant_admin], target_user_id: user.id) expect(logs).to be_present # It removes the redis keys for another user expect(AdminConfirmation.find_by_code(ac.token)).to eq(nil) expect(AdminConfirmation.exists_for?(user.id)).to eq(false) end - end - end diff --git a/spec/lib/admin_user_index_query_spec.rb b/spec/lib/admin_user_index_query_spec.rb index 71fe1856d8c..d603d3eaebb 100644 --- a/spec/lib/admin_user_index_query_spec.rb +++ b/spec/lib/admin_user_index_query_spec.rb @@ -2,7 +2,7 @@ RSpec.describe AdminUserIndexQuery do def real_users(query) - query.find_users_query.where('users.id > 0') + query.find_users_query.where("users.id > 0") end describe "sql order" do @@ -65,18 +65,15 @@ RSpec.describe AdminUserIndexQuery do end describe "no users with trust level" do - TrustLevel.levels.each do |key, value| it "#{key} returns no records" do query = ::AdminUserIndexQuery.new(query: key.to_s) expect(real_users(query)).to eq([]) end end - end describe "users with trust level" do - TrustLevel.levels.each do |key, value| it "finds user with trust #{key}" do user = Fabricate(:user, trust_level: value) @@ -88,107 +85,99 @@ RSpec.describe AdminUserIndexQuery do expect(real_users(query).to_a).to eq([user]) end end - end describe "with a pending user" do - fab!(:user) { Fabricate(:user, active: true, approved: false) } fab!(:inactive_user) { Fabricate(:user, approved: false, active: false) } it "finds the unapproved user" do - query = ::AdminUserIndexQuery.new(query: 'pending') + query = ::AdminUserIndexQuery.new(query: "pending") expect(query.find_users).to include(user) expect(query.find_users).not_to include(inactive_user) end - context 'with a suspended pending user' do - fab!(:suspended_user) { Fabricate(:user, approved: false, suspended_at: 1.hour.ago, suspended_till: 20.years.from_now) } + context "with a suspended pending user" do + fab!(:suspended_user) do + Fabricate( + :user, + approved: false, + suspended_at: 1.hour.ago, + suspended_till: 20.years.from_now, + ) + end it "doesn't return the suspended user" do - query = ::AdminUserIndexQuery.new(query: 'pending') + query = ::AdminUserIndexQuery.new(query: "pending") expect(query.find_users).not_to include(suspended_user) end end - end describe "correct order with nil values" do - before(:each) do - Fabricate(:user, email: "test2@example.com", last_emailed_at: 1.hour.ago) - end + before(:each) { Fabricate(:user, email: "test2@example.com", last_emailed_at: 1.hour.ago) } it "shows nil values first with asc" do users = ::AdminUserIndexQuery.new(order: "last_emailed", asc: true).find_users - expect(users.where('users.id > -2').count).to eq(2) - expect(users.where('users.id > -2').order('users.id asc').first.username).to eq("system") + expect(users.where("users.id > -2").count).to eq(2) + expect(users.where("users.id > -2").order("users.id asc").first.username).to eq("system") expect(users.first.last_emailed_at).to eq(nil) end it "shows nil values last with desc" do users = ::AdminUserIndexQuery.new(order: "last_emailed").find_users - expect(users.where('users.id > -2').count).to eq(2) + expect(users.where("users.id > -2").count).to eq(2) expect(users.first.last_emailed_at).to_not eq(nil) end - end describe "with an admin user" do - fab!(:user) { Fabricate(:user, admin: true) } fab!(:user2) { Fabricate(:user, admin: false) } it "finds the admin" do - query = ::AdminUserIndexQuery.new(query: 'admins') + query = ::AdminUserIndexQuery.new(query: "admins") expect(real_users(query)).to eq([user]) end - end describe "with a moderator" do - fab!(:user) { Fabricate(:user, moderator: true) } fab!(:user2) { Fabricate(:user, moderator: false) } it "finds the moderator" do - query = ::AdminUserIndexQuery.new(query: 'moderators') + query = ::AdminUserIndexQuery.new(query: "moderators") expect(real_users(query)).to eq([user]) end - end describe "with a silenced user" do - fab!(:user) { Fabricate(:user, silenced_till: 1.year.from_now) } fab!(:user2) { Fabricate(:user) } it "finds the silenced user" do - query = ::AdminUserIndexQuery.new(query: 'silenced') + query = ::AdminUserIndexQuery.new(query: "silenced") expect(real_users(query)).to eq([user]) end - end describe "with a staged user" do - fab!(:user) { Fabricate(:user, staged: true) } fab!(:user2) { Fabricate(:user, staged: false) } it "finds the staged user" do - query = ::AdminUserIndexQuery.new(query: 'staged') + query = ::AdminUserIndexQuery.new(query: "staged") expect(real_users(query)).to eq([user]) end - end describe "filtering" do - context "with exact email bypass" do it "can correctly bypass expensive ilike query" do - user = Fabricate(:user, email: 'sam@Sam.com') + user = Fabricate(:user, email: "sam@Sam.com") - query = AdminUserIndexQuery.new(filter: 'Sam@sam.com').find_users_query + query = AdminUserIndexQuery.new(filter: "Sam@sam.com").find_users_query expect(query.count).to eq(1) expect(query.first.id).to eq(user.id) @@ -196,22 +185,20 @@ RSpec.describe AdminUserIndexQuery do end it "can correctly bypass expensive ilike query" do - user = Fabricate(:user, email: 'sam2@Sam.com') + user = Fabricate(:user, email: "sam2@Sam.com") - query = AdminUserIndexQuery.new(email: 'Sam@sam.com').find_users_query + query = AdminUserIndexQuery.new(email: "Sam@sam.com").find_users_query expect(query.count).to eq(0) expect(query.to_sql.downcase).not_to include("ilike") - query = AdminUserIndexQuery.new(email: 'Sam2@sam.com').find_users_query + query = AdminUserIndexQuery.new(email: "Sam2@sam.com").find_users_query expect(query.first.id).to eq(user.id) expect(query.count).to eq(1) expect(query.to_sql.downcase).not_to include("ilike") - end end context "with email fragment" do - before(:each) { Fabricate(:user, email: "test1@example.com") } it "matches the email" do @@ -223,11 +210,9 @@ RSpec.describe AdminUserIndexQuery do query = ::AdminUserIndexQuery.new(filter: "Test1\t") expect(query.find_users.count()).to eq(1) end - end context "with username fragment" do - before(:each) { Fabricate(:user, username: "test_user_1") } it "matches the username" do @@ -242,15 +227,12 @@ RSpec.describe AdminUserIndexQuery do end context "with ip address fragment" do - fab!(:user) { Fabricate(:user, ip_address: "117.207.94.9") } it "matches the ip address" do query = ::AdminUserIndexQuery.new(filter: " 117.207.94.9 ") expect(query.find_users.count()).to eq(1) end - end - end end diff --git a/spec/lib/archetype_spec.rb b/spec/lib/archetype_spec.rb index 9b5a6b6945b..98430120729 100644 --- a/spec/lib/archetype_spec.rb +++ b/spec/lib/archetype_spec.rb @@ -1,36 +1,36 @@ # encoding: utf-8 # frozen_string_literal: true -require 'archetype' +require "archetype" RSpec.describe Archetype do - describe 'default archetype' do - it 'has an Archetype by default' do + describe "default archetype" do + it "has an Archetype by default" do expect(Archetype.list).to be_present end - it 'has an id of default' do + it "has an id of default" do expect(Archetype.list.first.id).to eq(Archetype.default) end - context 'with duplicate' do + context "with duplicate" do before do @old_size = Archetype.list.size Archetype.register(Archetype.default) end - it 'does not add the same archetype twice' do + it "does not add the same archetype twice" do expect(Archetype.list.size).to eq(@old_size) end end end - describe 'register an archetype' do - it 'has one more element' do + describe "register an archetype" do + it "has one more element" do @list = Archetype.list.dup - Archetype.register('glados') + Archetype.register("glados") expect(Archetype.list.size).to eq(@list.size + 1) - expect(Archetype.list.find { |a| a.id == 'glados' }).to be_present + expect(Archetype.list.find { |a| a.id == "glados" }).to be_present end end end diff --git a/spec/lib/auth/default_current_user_provider_spec.rb b/spec/lib/auth/default_current_user_provider_spec.rb index 9c542e7965a..39b84153f3b 100644 --- a/spec/lib/auth/default_current_user_provider_spec.rb +++ b/spec/lib/auth/default_current_user_provider_spec.rb @@ -54,17 +54,13 @@ RSpec.describe Auth::DefaultCurrentUserProvider do it "raises for a revoked key" do api_key = ApiKey.create! params = { "HTTP_API_USERNAME" => user.username.downcase, "HTTP_API_KEY" => api_key.key } - expect( - provider("/", params).current_user.id - ).to eq(user.id) + expect(provider("/", params).current_user.id).to eq(user.id) api_key.reload.update(revoked_at: Time.zone.now, last_used_at: nil) expect(api_key.reload.last_used_at).to eq(nil) params = { "HTTP_API_USERNAME" => user.username.downcase, "HTTP_API_KEY" => api_key.key } - expect { - provider("/", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/", params).current_user }.to raise_error(Discourse::InvalidAccess) api_key.reload expect(api_key.last_used_at).to eq(nil) @@ -72,9 +68,10 @@ RSpec.describe Auth::DefaultCurrentUserProvider do it "raises errors for incorrect api_key" do params = { "HTTP_API_KEY" => "INCORRECT" } - expect { - provider("/", params).current_user - }.to raise_error(Discourse::InvalidAccess, /API username or key is invalid/) + expect { provider("/", params).current_user }.to raise_error( + Discourse::InvalidAccess, + /API username or key is invalid/, + ) end it "finds a user for a correct per-user api key" do @@ -83,9 +80,9 @@ RSpec.describe Auth::DefaultCurrentUserProvider do good_provider = provider("/", params) - expect do - expect(good_provider.current_user.id).to eq(user.id) - end.to change { api_key.reload.last_used_at } + expect do expect(good_provider.current_user.id).to eq(user.id) end.to change { + api_key.reload.last_used_at + } expect(good_provider.is_api?).to eq(true) expect(good_provider.is_user_api?).to eq(false) @@ -93,15 +90,11 @@ RSpec.describe Auth::DefaultCurrentUserProvider do user.update_columns(active: false) - expect { - provider("/", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/", params).current_user }.to raise_error(Discourse::InvalidAccess) user.update_columns(active: true, suspended_till: 1.day.from_now) - expect { - provider("/", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/", params).current_user }.to raise_error(Discourse::InvalidAccess) end it "raises for a user pretending" do @@ -109,26 +102,22 @@ RSpec.describe Auth::DefaultCurrentUserProvider do api_key = ApiKey.create!(user_id: user.id, created_by_id: -1) params = { "HTTP_API_KEY" => api_key.key, "HTTP_API_USERNAME" => user2.username.downcase } - expect { - provider("/", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/", params).current_user }.to raise_error(Discourse::InvalidAccess) end it "raises for a user with a mismatching ip" do - api_key = ApiKey.create!(user_id: user.id, created_by_id: -1, allowed_ips: ['10.0.0.0/24']) + api_key = ApiKey.create!(user_id: user.id, created_by_id: -1, allowed_ips: ["10.0.0.0/24"]) params = { "HTTP_API_KEY" => api_key.key, "HTTP_API_USERNAME" => user.username.downcase, - "REMOTE_ADDR" => "10.1.0.1" + "REMOTE_ADDR" => "10.1.0.1", } - expect { - provider("/", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/", params).current_user }.to raise_error(Discourse::InvalidAccess) end it "allows a user with a matching ip" do - api_key = ApiKey.create!(user_id: user.id, created_by_id: -1, allowed_ips: ['100.0.0.0/24']) + api_key = ApiKey.create!(user_id: user.id, created_by_id: -1, allowed_ips: ["100.0.0.0/24"]) params = { "HTTP_API_KEY" => api_key.key, "HTTP_API_USERNAME" => user.username.downcase, @@ -142,7 +131,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do params = { "HTTP_API_KEY" => api_key.key, "HTTP_API_USERNAME" => user.username.downcase, - "HTTP_X_FORWARDED_FOR" => "10.1.1.1, 100.0.0.22" + "HTTP_X_FORWARDED_FOR" => "10.1.1.1, 100.0.0.22", } found_user = provider("/", params).current_user @@ -165,18 +154,18 @@ RSpec.describe Auth::DefaultCurrentUserProvider do it "finds a user for a correct system api key with external id" do api_key = ApiKey.create!(created_by_id: -1) - SingleSignOnRecord.create(user_id: user.id, external_id: "abc", last_payload: '') + SingleSignOnRecord.create(user_id: user.id, external_id: "abc", last_payload: "") params = { "HTTP_API_KEY" => api_key.key, "HTTP_API_USER_EXTERNAL_ID" => "abc" } expect(provider("/", params).current_user.id).to eq(user.id) end it "raises for a mismatched api_key header and param external id" do api_key = ApiKey.create!(created_by_id: -1) - SingleSignOnRecord.create(user_id: user.id, external_id: "abc", last_payload: '') + SingleSignOnRecord.create(user_id: user.id, external_id: "abc", last_payload: "") params = { "HTTP_API_KEY" => api_key.key } - expect { - provider("/?api_user_external_id=abc", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/?api_user_external_id=abc", params).current_user }.to raise_error( + Discourse::InvalidAccess, + ) end it "finds a user for a correct system api key with id" do @@ -188,19 +177,15 @@ RSpec.describe Auth::DefaultCurrentUserProvider do it "raises for a mismatched api_key header and param user id" do api_key = ApiKey.create!(created_by_id: -1) params = { "HTTP_API_KEY" => api_key.key } - expect { - provider("/?api_user_id=#{user.id}", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/?api_user_id=#{user.id}", params).current_user }.to raise_error( + Discourse::InvalidAccess, + ) end describe "when readonly mode is enabled due to postgres" do - before do - Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) - end + before { Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) } - after do - Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) - end + after { Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) } it "should not update ApiKey#last_used_at" do api_key = ApiKey.create!(user_id: user.id, created_by_id: -1) @@ -208,16 +193,14 @@ RSpec.describe Auth::DefaultCurrentUserProvider do good_provider = provider("/", params) - expect do - expect(good_provider.current_user.id).to eq(user.id) - end.to_not change { api_key.reload.last_used_at } + expect do expect(good_provider.current_user.id).to eq(user.id) end.to_not change { + api_key.reload.last_used_at + } end end context "with rate limiting" do - before do - RateLimiter.enable - end + before { RateLimiter.enable } it "rate limits admin api requests" do global_setting :max_admin_api_reqs_per_minute, 3 @@ -233,15 +216,15 @@ RSpec.describe Auth::DefaultCurrentUserProvider do provider("/", system_params).current_user provider("/", params).current_user - expect do - provider("/", system_params).current_user - end.to raise_error(RateLimiter::LimitExceeded) + expect do provider("/", system_params).current_user end.to raise_error( + RateLimiter::LimitExceeded, + ) freeze_time 59.seconds.from_now - expect do - provider("/", system_params).current_user - end.to raise_error(RateLimiter::LimitExceeded) + expect do provider("/", system_params).current_user end.to raise_error( + RateLimiter::LimitExceeded, + ) freeze_time 2.seconds.from_now @@ -259,7 +242,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do describe "#current_user" do let(:cookie) do - new_provider = provider('/') + new_provider = provider("/") new_provider.log_on_user(user, {}, new_provider.cookie_jar) CGI.escape(new_provider.cookie_jar["_t"]) end @@ -269,9 +252,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do user.clear_last_seen_cache!(@orig) end - after do - user.clear_last_seen_cache!(@orig) - end + after { user.clear_last_seen_cache!(@orig) } it "should not update last seen for suspended users" do provider2 = provider("/", "HTTP_COOKIE" => "_t=#{cookie}") @@ -295,13 +276,9 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end describe "when readonly mode is enabled due to postgres" do - before do - Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) - end + before { Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) } - after do - Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) - end + after { Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) } it "should not update User#last_seen_at" do provider2 = provider("/", "HTTP_COOKIE" => "_t=#{cookie}") @@ -338,35 +315,47 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end it "should update ajax reqs with discourse visible" do - expect(provider("/topic/anything/goes", - :method => "POST", - "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", - "HTTP_DISCOURSE_PRESENT" => "true" - ).should_update_last_seen?).to eq(true) + expect( + provider( + "/topic/anything/goes", + :method => "POST", + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "HTTP_DISCOURSE_PRESENT" => "true", + ).should_update_last_seen?, + ).to eq(true) end it "should not update last seen for ajax calls without Discourse-Present header" do - expect(provider("/topic/anything/goes", - :method => "POST", - "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" - ).should_update_last_seen?).to eq(false) + expect( + provider( + "/topic/anything/goes", + :method => "POST", + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + ).should_update_last_seen?, + ).to eq(false) end it "should update last seen for API calls with Discourse-Present header" do api_key = ApiKey.create!(user_id: user.id, created_by_id: -1) - params = { :method => "POST", - "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", - "HTTP_API_KEY" => api_key.key - } + params = { + :method => "POST", + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "HTTP_API_KEY" => api_key.key, + } expect(provider("/topic/anything/goes", params).should_update_last_seen?).to eq(false) - expect(provider("/topic/anything/goes", params.merge("HTTP_DISCOURSE_PRESENT" => "true")).should_update_last_seen?).to eq(true) + expect( + provider( + "/topic/anything/goes", + params.merge("HTTP_DISCOURSE_PRESENT" => "true"), + ).should_update_last_seen?, + ).to eq(true) end it "supports non persistent sessions" do SiteSetting.persistent_sessions = false - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) cookie_info = get_cookie_info(@provider.cookie_jar, "_t") @@ -377,12 +366,12 @@ RSpec.describe Auth::DefaultCurrentUserProvider do token = UserAuthToken.generate!(user_id: user.id).unhashed_auth_token ip = "10.0.0.1" env = { "HTTP_COOKIE" => "_t=#{token}", "REMOTE_ADDR" => ip } - expect(provider('/', env).current_user.id).to eq(user.id) + expect(provider("/", env).current_user.id).to eq(user.id) end it "correctly rotates tokens" do SiteSetting.maximum_session_age = 3 - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) cookie = @provider.cookie_jar["_t"] @@ -405,12 +394,8 @@ RSpec.describe Auth::DefaultCurrentUserProvider do expect(token.auth_token_seen).to eq(true) provider2.refresh_session(user, {}, provider2.cookie_jar) - expect( - decrypt_auth_cookie(provider2.cookie_jar["_t"])[:token] - ).not_to eq(unhashed_token) - expect( - decrypt_auth_cookie(provider2.cookie_jar["_t"])[:token].size - ).to eq(32) + expect(decrypt_auth_cookie(provider2.cookie_jar["_t"])[:token]).not_to eq(unhashed_token) + expect(decrypt_auth_cookie(provider2.cookie_jar["_t"])[:token].size).to eq(32) token.reload expect(token.auth_token_seen).to eq(false) @@ -432,23 +417,20 @@ RSpec.describe Auth::DefaultCurrentUserProvider do # assume it never reached the client expect(token.prev_auth_token).to eq(old_token) expect(token.auth_token).not_to eq(unverified_token) - end describe "events" do before do @refreshes = 0 - @increase_refreshes = -> (user) { @refreshes += 1 } + @increase_refreshes = ->(user) { @refreshes += 1 } DiscourseEvent.on(:user_session_refreshed, &@increase_refreshes) end - after do - DiscourseEvent.off(:user_session_refreshed, &@increase_refreshes) - end + after { DiscourseEvent.off(:user_session_refreshed, &@increase_refreshes) } it "fires event when updating last seen" do - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) cookie = @provider.cookie_jar["_t"] unhashed_token = decrypt_auth_cookie(cookie)[:token] @@ -460,7 +442,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end it "does not fire an event when last seen does not update" do - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) cookie = @provider.cookie_jar["_t"] unhashed_token = decrypt_auth_cookie(cookie)[:token] @@ -473,42 +455,38 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end describe "rate limiting" do - before do - RateLimiter.enable - end + before { RateLimiter.enable } it "can only try 10 bad cookies a minute" do token = UserAuthToken.generate!(user_id: user.id) - cookie = create_auth_cookie( - token: token.unhashed_auth_token, - user_id: user.id, - trust_level: user.trust_level, - issued_at: 5.minutes.ago - ) + cookie = + create_auth_cookie( + token: token.unhashed_auth_token, + user_id: user.id, + trust_level: user.trust_level, + issued_at: 5.minutes.ago, + ) - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) RateLimiter.new(nil, "cookie_auth_10.0.0.1", 10, 60).clear! RateLimiter.new(nil, "cookie_auth_10.0.0.2", 10, 60).clear! ip = "10.0.0.1" - bad_cookie = create_auth_cookie( - token: SecureRandom.hex, - user_id: user.id, - trust_level: user.trust_level, - issued_at: 5.minutes.ago, - ) + bad_cookie = + create_auth_cookie( + token: SecureRandom.hex, + user_id: user.id, + trust_level: user.trust_level, + issued_at: 5.minutes.ago, + ) env = { "HTTP_COOKIE" => "_t=#{bad_cookie}", "REMOTE_ADDR" => ip } - 10.times do - provider('/', env).current_user - end + 10.times { provider("/", env).current_user } - expect { - provider('/', env).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/", env).current_user }.to raise_error(Discourse::InvalidAccess) expect { env["HTTP_COOKIE"] = "_t=#{cookie}" @@ -517,29 +495,28 @@ RSpec.describe Auth::DefaultCurrentUserProvider do env["REMOTE_ADDR"] = "10.0.0.2" - expect { - provider('/', env).current_user - }.not_to raise_error + expect { provider("/", env).current_user }.not_to raise_error end end it "correctly removes invalid cookies" do - bad_cookie = create_auth_cookie( - token: SecureRandom.hex, - user_id: 1, - trust_level: 4, - issued_at: 5.minutes.ago, - ) - @provider = provider('/') + bad_cookie = + create_auth_cookie( + token: SecureRandom.hex, + user_id: 1, + trust_level: 4, + issued_at: 5.minutes.ago, + ) + @provider = provider("/") @provider.cookie_jar["_t"] = bad_cookie @provider.refresh_session(nil, {}, @provider.cookie_jar) expect(@provider.cookie_jar.key?("_t")).to eq(false) end it "logging on user always creates a new token" do - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) - @provider2 = provider('/') + @provider2 = provider("/") @provider2.log_on_user(user, {}, @provider2.cookie_jar) expect(UserAuthToken.where(user_id: user.id).count).to eq(2) @@ -548,22 +525,24 @@ RSpec.describe Auth::DefaultCurrentUserProvider do it "cleans up old sessions when a user logs in" do yesterday = 1.day.ago - UserAuthToken.insert_all((1..(UserAuthToken::MAX_SESSION_COUNT + 2)).to_a.map do |i| - { - user_id: user.id, - created_at: yesterday + i.seconds, - updated_at: yesterday + i.seconds, - rotated_at: yesterday + i.seconds, - prev_auth_token: "abc#{i}", - auth_token: "abc#{i}" - } - end) + UserAuthToken.insert_all( + (1..(UserAuthToken::MAX_SESSION_COUNT + 2)).to_a.map do |i| + { + user_id: user.id, + created_at: yesterday + i.seconds, + updated_at: yesterday + i.seconds, + rotated_at: yesterday + i.seconds, + prev_auth_token: "abc#{i}", + auth_token: "abc#{i}", + } + end, + ) # Check the oldest 3 still exist expect(UserAuthToken.where(auth_token: (1..3).map { |i| "abc#{i}" }).count).to eq(3) # On next login, gets fixed - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) expect(UserAuthToken.where(user_id: user.id).count).to eq(UserAuthToken::MAX_SESSION_COUNT) @@ -575,7 +554,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do SiteSetting.force_https = false SiteSetting.same_site_cookies = "Lax" - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) cookie_info = get_cookie_info(@provider.cookie_jar, "_t") @@ -586,7 +565,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do SiteSetting.force_https = true SiteSetting.same_site_cookies = "Disabled" - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) cookie_info = get_cookie_info(@provider.cookie_jar, "_t") @@ -597,14 +576,15 @@ RSpec.describe Auth::DefaultCurrentUserProvider do it "correctly expires session" do SiteSetting.maximum_session_age = 2 token = UserAuthToken.generate!(user_id: user.id) - cookie = create_auth_cookie( - token: token.unhashed_auth_token, - user_id: user.id, - trust_level: user.trust_level, - issued_at: 5.minutes.ago - ) + cookie = + create_auth_cookie( + token: token.unhashed_auth_token, + user_id: user.id, + trust_level: user.trust_level, + issued_at: 5.minutes.ago, + ) - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) expect(provider("/", "HTTP_COOKIE" => "_t=#{cookie}").current_user.id).to eq(user.id) @@ -628,20 +608,21 @@ RSpec.describe Auth::DefaultCurrentUserProvider do let :api_key do UserApiKey.create!( - application_name: 'my app', - client_id: '1234', - scopes: ['read'].map { |name| UserApiKeyScope.new(name: name) }, - user_id: user.id + application_name: "my app", + client_id: "1234", + scopes: ["read"].map { |name| UserApiKeyScope.new(name: name) }, + user_id: user.id, ) end it "can clear old duplicate keys correctly" do - dupe = UserApiKey.create!( - application_name: 'my app', - client_id: '12345', - scopes: ['read'].map { |name| UserApiKeyScope.new(name: name) }, - user_id: user.id - ) + dupe = + UserApiKey.create!( + application_name: "my app", + client_id: "12345", + scopes: ["read"].map { |name| UserApiKeyScope.new(name: name) }, + user_id: user.id, + ) params = { "REQUEST_METHOD" => "GET", @@ -655,16 +636,13 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end it "allows user API access correctly" do - params = { - "REQUEST_METHOD" => "GET", - "HTTP_USER_API_KEY" => api_key.key, - } + params = { "REQUEST_METHOD" => "GET", "HTTP_USER_API_KEY" => api_key.key } good_provider = provider("/", params) - expect do - expect(good_provider.current_user.id).to eq(user.id) - end.to change { api_key.reload.last_used_at } + expect do expect(good_provider.current_user.id).to eq(user.id) end.to change { + api_key.reload.last_used_at + } expect(good_provider.is_api?).to eq(false) expect(good_provider.is_user_api?).to eq(true) @@ -676,38 +654,27 @@ RSpec.describe Auth::DefaultCurrentUserProvider do user.update_columns(suspended_till: 1.year.from_now) - expect { - provider("/", params).current_user - }.to raise_error(Discourse::InvalidAccess) + expect { provider("/", params).current_user }.to raise_error(Discourse::InvalidAccess) end describe "when readonly mode is enabled due to postgres" do - before do - Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) - end + before { Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) } - after do - Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) - end + after { Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) } - it 'should not update ApiKey#last_used_at' do - params = { - "REQUEST_METHOD" => "GET", - "HTTP_USER_API_KEY" => api_key.key, - } + it "should not update ApiKey#last_used_at" do + params = { "REQUEST_METHOD" => "GET", "HTTP_USER_API_KEY" => api_key.key } good_provider = provider("/", params) - expect do - expect(good_provider.current_user.id).to eq(user.id) - end.to_not change { api_key.reload.last_used_at } + expect do expect(good_provider.current_user.id).to eq(user.id) end.to_not change { + api_key.reload.last_used_at + } end end context "with rate limiting" do - before do - RateLimiter.enable - end + before { RateLimiter.enable } it "rate limits api usage" do limiter1 = RateLimiter.new(nil, "user_api_day_#{ApiKey.hash_key(api_key.key)}", 10, 60) @@ -718,18 +685,11 @@ RSpec.describe Auth::DefaultCurrentUserProvider do global_setting :max_user_api_reqs_per_day, 3 global_setting :max_user_api_reqs_per_minute, 4 - params = { - "REQUEST_METHOD" => "GET", - "HTTP_USER_API_KEY" => api_key.key, - } + params = { "REQUEST_METHOD" => "GET", "HTTP_USER_API_KEY" => api_key.key } - 3.times do - provider("/", params).current_user - end + 3.times { provider("/", params).current_user } - expect { - provider("/", params).current_user - }.to raise_error(RateLimiter::LimitExceeded) + expect { provider("/", params).current_user }.to raise_error(RateLimiter::LimitExceeded) global_setting :max_user_api_reqs_per_day, 4 global_setting :max_user_api_reqs_per_minute, 3 @@ -737,19 +697,15 @@ RSpec.describe Auth::DefaultCurrentUserProvider do limiter1.clear! limiter2.clear! - 3.times do - provider("/", params).current_user - end + 3.times { provider("/", params).current_user } - expect { - provider("/", params).current_user - }.to raise_error(RateLimiter::LimitExceeded) + expect { provider("/", params).current_user }.to raise_error(RateLimiter::LimitExceeded) end end end it "ignores a valid auth cookie that has been tampered with" do - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) cookie = @provider.cookie_jar["_t"] @@ -757,33 +713,38 @@ RSpec.describe Auth::DefaultCurrentUserProvider do ip = "10.0.0.1" env = { "HTTP_COOKIE" => "_t=#{cookie}", "REMOTE_ADDR" => ip } - expect(provider('/', env).current_user).to eq(nil) + expect(provider("/", env).current_user).to eq(nil) end it "copes with json-serialized auth cookies" do # We're switching to :json during the Rails 7 upgrade, but we want a clean revert path # back to Rails 6 if needed - @provider = provider('/', { # The upcoming default - ActionDispatch::Cookies::COOKIES_SERIALIZER => :json, - method: "GET", - }) + @provider = + provider( + "/", + { # The upcoming default + ActionDispatch::Cookies::COOKIES_SERIALIZER => :json, + :method => "GET", + }, + ) @provider.log_on_user(user, {}, @provider.cookie_jar) cookie = CGI.escape(@provider.cookie_jar["_t"]) ip = "10.0.0.1" env = { "HTTP_COOKIE" => "_t=#{cookie}", "REMOTE_ADDR" => ip } - provider2 = provider('/', env) + provider2 = provider("/", env) expect(provider2.current_user).to eq(user) expect(provider2.cookie_jar.encrypted["_t"].keys).to include("user_id", "token") # (strings) end describe "#log_off_user" do it "should work when the current user was cached by a different provider instance" do - user_provider = provider('/') + user_provider = provider("/") user_provider.log_on_user(user, {}, user_provider.cookie_jar) cookie = CGI.escape(user_provider.cookie_jar["_t"]) - env = create_request_env(path: "/").merge({ method: "GET", "HTTP_COOKIE" => "_t=#{cookie}" }) + env = + create_request_env(path: "/").merge({ :method => "GET", "HTTP_COOKIE" => "_t=#{cookie}" }) user_provider = TestProvider.new(env) expect(user_provider.current_user).to eq(user) @@ -802,7 +763,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end it "makes the user into an admin if their email is in DISCOURSE_DEVELOPER_EMAILS" do - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) expect(user.reload.admin).to eq(true) user2 = Fabricate(:user) @@ -811,7 +772,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end it "adds the user to the correct staff/admin auto groups" do - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) user.reload expect(user.in_any_groups?([Group::AUTO_GROUPS[:staff]])).to eq(true) @@ -819,7 +780,7 @@ RSpec.describe Auth::DefaultCurrentUserProvider do end it "runs the job to enable bootstrap mode" do - @provider = provider('/') + @provider = provider("/") @provider.log_on_user(user, {}, @provider.cookie_jar) expect_job_enqueued(job: :enable_bootstrap_mode, args: { user_id: user.id }) end diff --git a/spec/lib/auth/discord_authenticator_spec.rb b/spec/lib/auth/discord_authenticator_spec.rb index cff7c662f49..7aea3e5d094 100644 --- a/spec/lib/auth/discord_authenticator_spec.rb +++ b/spec/lib/auth/discord_authenticator_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Auth::DiscordAuthenticator do - let(:hash) { + let(:hash) do OmniAuth::AuthHash.new( provider: "facebook", extra: { @@ -10,27 +10,27 @@ RSpec.describe Auth::DiscordAuthenticator do username: "bobbob", guilds: [ { - "id": "80351110224678912", - "name": "1337 Krew", - "icon": "8342729096ea3675442027381ff50dfe", - "owner": true, - "permissions": 36953089 - } - ] - } + id: "80351110224678912", + name: "1337 Krew", + icon: "8342729096ea3675442027381ff50dfe", + owner: true, + permissions: 36_953_089, + }, + ], + }, }, info: { email: "bob@bob.com", - name: "bobbob" + name: "bobbob", }, - uid: "100" + uid: "100", ) - } + end let(:authenticator) { described_class.new } - describe 'after_authenticate' do - it 'works normally' do + describe "after_authenticate" do + it "works normally" do result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) expect(result.failed).to eq(false) @@ -38,16 +38,16 @@ RSpec.describe Auth::DiscordAuthenticator do expect(result.email).to eq("bob@bob.com") end - it 'denies access when guilds are restricted' do - SiteSetting.discord_trusted_guilds = ["someguildid", "someotherguildid"].join("|") + it "denies access when guilds are restricted" do + SiteSetting.discord_trusted_guilds = %w[someguildid someotherguildid].join("|") result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) expect(result.failed).to eq(true) expect(result.failed_reason).to eq(I18n.t("discord.not_in_allowed_guild")) end - it 'allows access when in an allowed guild' do - SiteSetting.discord_trusted_guilds = ["80351110224678912", "anothertrustedguild"].join("|") + it "allows access when in an allowed guild" do + SiteSetting.discord_trusted_guilds = %w[80351110224678912 anothertrustedguild].join("|") result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) expect(result.failed).to eq(false) diff --git a/spec/lib/auth/facebook_authenticator_spec.rb b/spec/lib/auth/facebook_authenticator_spec.rb index 9033506af33..d19319d3a1d 100644 --- a/spec/lib/auth/facebook_authenticator_spec.rb +++ b/spec/lib/auth/facebook_authenticator_spec.rb @@ -1,31 +1,32 @@ # frozen_string_literal: true RSpec.describe Auth::FacebookAuthenticator do - let(:hash) { + let(:hash) do { provider: "facebook", extra: { - raw_info: {} + raw_info: { + }, }, info: { email: "bob@bob.com", first_name: "Bob", - last_name: "Smith" + last_name: "Smith", }, - uid: "100" + uid: "100", } - } + end let(:authenticator) { Auth::FacebookAuthenticator.new } - describe 'after_authenticate' do - it 'can authenticate and create a user record for already existing users' do + describe "after_authenticate" do + it "can authenticate and create a user record for already existing users" do user = Fabricate(:user) result = authenticator.after_authenticate(hash.deep_merge(info: { email: user.email })) expect(result.user.id).to eq(user.id) end - it 'can connect to a different existing user account' do + it "can connect to a different existing user account" do user1 = Fabricate(:user) user2 = Fabricate(:user) @@ -34,46 +35,64 @@ RSpec.describe Auth::FacebookAuthenticator do result = authenticator.after_authenticate(hash, existing_account: user2) expect(result.user.id).to eq(user2.id) - expect(UserAssociatedAccount.exists?(provider_name: "facebook", user_id: user1.id)).to eq(false) - expect(UserAssociatedAccount.exists?(provider_name: "facebook", user_id: user2.id)).to eq(true) + expect(UserAssociatedAccount.exists?(provider_name: "facebook", user_id: user1.id)).to eq( + false, + ) + expect(UserAssociatedAccount.exists?(provider_name: "facebook", user_id: user2.id)).to eq( + true, + ) end - it 'can create a proper result for non existing users' do + it "can create a proper result for non existing users" do result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) expect(result.name).to eq("Bob Smith") end end - describe 'description_for_user' do + describe "description_for_user" do fab!(:user) { Fabricate(:user) } - it 'returns empty string if no entry for user' do + it "returns empty string if no entry for user" do expect(authenticator.description_for_user(user)).to eq("") end - it 'returns correct information' do - UserAssociatedAccount.create!(provider_name: "facebook", user_id: user.id, provider_uid: 100, info: { email: "someuser@somedomain.tld" }) - expect(authenticator.description_for_user(user)).to eq('someuser@somedomain.tld') + it "returns correct information" do + UserAssociatedAccount.create!( + provider_name: "facebook", + user_id: user.id, + provider_uid: 100, + info: { + email: "someuser@somedomain.tld", + }, + ) + expect(authenticator.description_for_user(user)).to eq("someuser@somedomain.tld") end end - describe 'revoke' do + describe "revoke" do fab!(:user) { Fabricate(:user) } let(:authenticator) { Auth::FacebookAuthenticator.new } - it 'raises exception if no entry for user' do + it "raises exception if no entry for user" do expect { authenticator.revoke(user) }.to raise_error(Discourse::NotFound) end context "with valid record" do before do - SiteSetting.facebook_app_id = '123' - SiteSetting.facebook_app_secret = 'abcde' - UserAssociatedAccount.create!(provider_name: "facebook", user_id: user.id, provider_uid: 100, info: { email: "someuser@somedomain.tld" }) + SiteSetting.facebook_app_id = "123" + SiteSetting.facebook_app_secret = "abcde" + UserAssociatedAccount.create!( + provider_name: "facebook", + user_id: user.id, + provider_uid: 100, + info: { + email: "someuser@somedomain.tld", + }, + ) end - it 'revokes correctly' do + it "revokes correctly" do expect(authenticator.description_for_user(user)).to eq("someuser@somedomain.tld") expect(authenticator.can_revoke?).to eq(true) expect(authenticator.revoke(user)).to eq(true) diff --git a/spec/lib/auth/github_authenticator_spec.rb b/spec/lib/auth/github_authenticator_spec.rb index 15020a96367..b5ec3162fa6 100644 --- a/spec/lib/auth/github_authenticator_spec.rb +++ b/spec/lib/auth/github_authenticator_spec.rb @@ -4,11 +4,7 @@ def auth_token_for(user) { provider: "github", extra: { - all_emails: [{ - email: user.email, - primary: true, - verified: true, - }] + all_emails: [{ email: user.email, primary: true, verified: true }], }, info: { email: user.email, @@ -16,7 +12,7 @@ def auth_token_for(user) name: user.name, image: "https://avatars3.githubusercontent.com/u/#{user.username}", }, - uid: '100' + uid: "100", } end @@ -24,10 +20,10 @@ RSpec.describe Auth::GithubAuthenticator do let(:authenticator) { described_class.new } fab!(:user) { Fabricate(:user) } - describe 'after_authenticate' do + describe "after_authenticate" do let(:data) { auth_token_for(user) } - it 'can authenticate and create a user record for already existing users' do + it "can authenticate and create a user record for already existing users" do result = authenticator.after_authenticate(data) expect(result.user.id).to eq(user.id) @@ -43,37 +39,41 @@ RSpec.describe Auth::GithubAuthenticator do expect(result.email_valid).to eq(true) end - it 'can authenticate and update GitHub screen_name for existing user' do - UserAssociatedAccount.create!(user_id: user.id, provider_name: "github", provider_uid: 100, info: { nickname: "boris" }) + it "can authenticate and update GitHub screen_name for existing user" do + UserAssociatedAccount.create!( + user_id: user.id, + provider_name: "github", + provider_uid: 100, + info: { + nickname: "boris", + }, + ) result = authenticator.after_authenticate(data) expect(result.user.id).to eq(user.id) expect(result.email).to eq(user.email) expect(result.email_valid).to eq(true) - expect(UserAssociatedAccount.find_by(provider_name: "github", user_id: user.id).info["nickname"]).to eq(user.username) + expect( + UserAssociatedAccount.find_by(provider_name: "github", user_id: user.id).info["nickname"], + ).to eq(user.username) end - it 'should use primary email for new user creation over other available emails' do + it "should use primary email for new user creation over other available emails" do hash = { provider: "github", extra: { - all_emails: [{ - email: "bob@example.com", - primary: false, - verified: true, - }, { - email: "john@example.com", - primary: true, - verified: true, - }] + all_emails: [ + { email: "bob@example.com", primary: false, verified: true }, + { email: "john@example.com", primary: true, verified: true }, + ], }, info: { email: "john@example.com", nickname: "john", name: "John Bob", }, - uid: "100" + uid: "100", } result = authenticator.after_authenticate(hash) @@ -81,28 +81,30 @@ RSpec.describe Auth::GithubAuthenticator do expect(result.email).to eq("john@example.com") end - it 'should not error out if user already has a different old github account attached' do - + it "should not error out if user already has a different old github account attached" do # There is a rare case where an end user had # 2 different github accounts and moved emails between the 2 - UserAssociatedAccount.create!(user_id: user.id, info: { nickname: 'bob' }, provider_uid: 100, provider_name: "github") + UserAssociatedAccount.create!( + user_id: user.id, + info: { + nickname: "bob", + }, + provider_uid: 100, + provider_name: "github", + ) hash = { provider: "github", extra: { - all_emails: [{ - email: user.email, - primary: false, - verified: true, - }] + all_emails: [{ email: user.email, primary: false, verified: true }], }, info: { email: "john@example.com", nickname: "john", name: "John Bob", }, - uid: "1001" + uid: "1001", } result = authenticator.after_authenticate(hash) @@ -111,22 +113,18 @@ RSpec.describe Auth::GithubAuthenticator do expect(UserAssociatedAccount.where(user_id: user.id).pluck(:provider_uid)).to eq(["1001"]) end - it 'will not authenticate for already existing users with an unverified email' do + it "will not authenticate for already existing users with an unverified email" do hash = { provider: "github", extra: { - all_emails: [{ - email: user.email, - primary: true, - verified: false, - }] + all_emails: [{ email: user.email, primary: true, verified: false }], }, info: { email: user.email, nickname: user.username, name: user.name, }, - uid: "100" + uid: "100", } result = authenticator.after_authenticate(hash) @@ -138,22 +136,18 @@ RSpec.describe Auth::GithubAuthenticator do expect(result.email_valid).to eq(false) end - it 'can create a proper result for non existing users' do + it "can create a proper result for non existing users" do hash = { provider: "github", extra: { - all_emails: [{ - email: "person@example.com", - primary: true, - verified: true, - }] + all_emails: [{ email: "person@example.com", primary: true, verified: true }], }, info: { email: "person@example.com", nickname: "person", name: "Person Lastname", }, - uid: "100" + uid: "100", } result = authenticator.after_authenticate(hash) @@ -165,26 +159,21 @@ RSpec.describe Auth::GithubAuthenticator do expect(result.email_valid).to eq(hash[:info][:email].present?) end - it 'will skip blocklisted domains for non existing users' do + it "will skip blocklisted domains for non existing users" do hash = { provider: "github", extra: { - all_emails: [{ - email: "not_allowed@blocklist.com", - primary: true, - verified: true, - }, { - email: "allowed@allowlist.com", - primary: false, - verified: true, - }] + all_emails: [ + { email: "not_allowed@blocklist.com", primary: true, verified: true }, + { email: "allowed@allowlist.com", primary: false, verified: true }, + ], }, info: { email: "not_allowed@blocklist.com", nickname: "person", name: "Person Lastname", }, - uid: "100" + uid: "100", } SiteSetting.blocked_email_domains = "blocklist.com" @@ -197,30 +186,22 @@ RSpec.describe Auth::GithubAuthenticator do expect(result.email_valid).to eq(true) end - it 'will find allowlisted domains for non existing users' do + it "will find allowlisted domains for non existing users" do hash = { provider: "github", extra: { - all_emails: [{ - email: "person@example.com", - primary: true, - verified: true, - }, { - email: "not_allowed@blocklist.com", - primary: false, - verified: true, - }, { - email: "allowed@allowlist.com", - primary: false, - verified: true, - }] + all_emails: [ + { email: "person@example.com", primary: true, verified: true }, + { email: "not_allowed@blocklist.com", primary: false, verified: true }, + { email: "allowed@allowlist.com", primary: false, verified: true }, + ], }, info: { email: "person@example.com", nickname: "person", name: "Person Lastname", }, - uid: "100" + uid: "100", } SiteSetting.allowed_email_domains = "allowlist.com" @@ -233,13 +214,20 @@ RSpec.describe Auth::GithubAuthenticator do expect(result.email_valid).to eq(true) end - it 'can connect to a different existing user account' do + it "can connect to a different existing user account" do user1 = Fabricate(:user) user2 = Fabricate(:user) expect(authenticator.can_connect_existing_user?).to eq(true) - UserAssociatedAccount.create!(provider_name: "github", user_id: user1.id, provider_uid: 100, info: { nickname: "boris" }) + UserAssociatedAccount.create!( + provider_name: "github", + user_id: user1.id, + provider_uid: 100, + info: { + nickname: "boris", + }, + ) result = authenticator.after_authenticate(data, existing_account: user2) @@ -249,47 +237,55 @@ RSpec.describe Auth::GithubAuthenticator do end end - describe 'revoke' do + describe "revoke" do fab!(:user) { Fabricate(:user) } let(:authenticator) { Auth::GithubAuthenticator.new } - it 'raises exception if no entry for user' do + it "raises exception if no entry for user" do expect { authenticator.revoke(user) }.to raise_error(Discourse::NotFound) end - it 'revokes correctly' do - UserAssociatedAccount.create!(provider_name: "github", user_id: user.id, provider_uid: 100, info: { nickname: "boris" }) + it "revokes correctly" do + UserAssociatedAccount.create!( + provider_name: "github", + user_id: user.id, + provider_uid: 100, + info: { + nickname: "boris", + }, + ) expect(authenticator.can_revoke?).to eq(true) expect(authenticator.revoke(user)).to eq(true) expect(authenticator.description_for_user(user)).to eq("") end end - describe 'avatar retrieval' do + describe "avatar retrieval" do let(:job_klass) { Jobs::DownloadAvatarFromUrl } - context 'when user has a custom avatar' do + context "when user has a custom avatar" do fab!(:user_avatar) { Fabricate(:user_avatar, custom_upload: Fabricate(:upload)) } fab!(:user_with_custom_avatar) { Fabricate(:user, user_avatar: user_avatar) } - it 'does not enqueue a download_avatar_from_url job' do + it "does not enqueue a download_avatar_from_url job" do expect { authenticator.after_authenticate(auth_token_for(user_with_custom_avatar)) }.to_not change(job_klass.jobs, :size) end end - context 'when user does not have a custom avatar' do - it 'enqueues a download_avatar_from_url job' do - expect { - authenticator.after_authenticate(auth_token_for(user)) - }.to change(job_klass.jobs, :size).by(1) + context "when user does not have a custom avatar" do + it "enqueues a download_avatar_from_url job" do + expect { authenticator.after_authenticate(auth_token_for(user)) }.to change( + job_klass.jobs, + :size, + ).by(1) - job_args = job_klass.jobs.last['args'].first + job_args = job_klass.jobs.last["args"].first - expect(job_args['url']).to eq("https://avatars3.githubusercontent.com/u/#{user.username}") - expect(job_args['user_id']).to eq(user.id) - expect(job_args['override_gravatar']).to eq(false) + expect(job_args["url"]).to eq("https://avatars3.githubusercontent.com/u/#{user.username}") + expect(job_args["user_id"]).to eq(user.id) + expect(job_args["override_gravatar"]).to eq(false) end end end diff --git a/spec/lib/auth/google_oauth2_authenticator_spec.rb b/spec/lib/auth/google_oauth2_authenticator_spec.rb index f610763ed35..d42a08d1d12 100644 --- a/spec/lib/auth/google_oauth2_authenticator_spec.rb +++ b/spec/lib/auth/google_oauth2_authenticator_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Auth::GoogleOAuth2Authenticator do - it 'does not look up user unless email is verified' do + it "does not look up user unless email is verified" do # note, emails that come back from google via omniauth are always valid # this protects against future regressions @@ -12,16 +12,16 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do provider: "google_oauth2", uid: "123456789", info: { - name: "John Doe", - email: user.email + name: "John Doe", + email: user.email, }, extra: { raw_info: { email: user.email, email_verified: false, - name: "John Doe" - } - } + name: "John Doe", + }, + }, } result = authenticator.after_authenticate(hash) @@ -29,8 +29,8 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do expect(result.user).to eq(nil) end - describe 'after_authenticate' do - it 'can authenticate and create a user record for already existing users' do + describe "after_authenticate" do + it "can authenticate and create a user record for already existing users" do authenticator = Auth::GoogleOAuth2Authenticator.new user = Fabricate(:user) @@ -38,16 +38,16 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do provider: "google_oauth2", uid: "123456789", info: { - name: "John Doe", - email: user.email + name: "John Doe", + email: user.email, }, extra: { raw_info: { email: user.email, email_verified: true, - name: "John Doe" - } - } + name: "John Doe", + }, + }, } result = authenticator.after_authenticate(hash) @@ -55,27 +55,31 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do expect(result.user.id).to eq(user.id) end - it 'can connect to a different existing user account' do + it "can connect to a different existing user account" do authenticator = Auth::GoogleOAuth2Authenticator.new user1 = Fabricate(:user) user2 = Fabricate(:user) - UserAssociatedAccount.create!(provider_name: "google_oauth2", user_id: user1.id, provider_uid: 100) + UserAssociatedAccount.create!( + provider_name: "google_oauth2", + user_id: user1.id, + provider_uid: 100, + ) hash = { provider: "google_oauth2", uid: "100", info: { - name: "John Doe", - email: user1.email + name: "John Doe", + email: user1.email, }, extra: { raw_info: { email: user1.email, email_verified: true, - name: "John Doe" - } - } + name: "John Doe", + }, + }, } result = authenticator.after_authenticate(hash, existing_account: user2) @@ -85,23 +89,23 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do expect(UserAssociatedAccount.exists?(user_id: user2.id)).to eq(true) end - it 'can create a proper result for non existing users' do + it "can create a proper result for non existing users" do hash = { provider: "google_oauth2", uid: "123456789", info: { - first_name: "Jane", - last_name: "Doe", - name: "Jane Doe", - email: "jane.doe@the.google.com" + first_name: "Jane", + last_name: "Doe", + name: "Jane Doe", + email: "jane.doe@the.google.com", }, extra: { raw_info: { email: "jane.doe@the.google.com", email_verified: true, - name: "Jane Doe" - } - } + name: "Jane Doe", + }, + }, } authenticator = described_class.new @@ -117,48 +121,38 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do group1 = OmniAuth::AuthHash.new(id: "12345", name: "group1") group2 = OmniAuth::AuthHash.new(id: "67890", name: "group2") @groups = [group1, group2] - @auth_hash = OmniAuth::AuthHash.new( - provider: "google_oauth2", - uid: "123456789", - info: { - first_name: "Jane", - last_name: "Doe", - name: "Jane Doe", - email: "jane.doe@the.google.com" - }, - extra: { - raw_info: { + @auth_hash = + OmniAuth::AuthHash.new( + provider: "google_oauth2", + uid: "123456789", + info: { + first_name: "Jane", + last_name: "Doe", + name: "Jane Doe", email: "jane.doe@the.google.com", - email_verified: true, - name: "Jane Doe" }, - } - ) + extra: { + raw_info: { + email: "jane.doe@the.google.com", + email_verified: true, + name: "Jane Doe", + }, + }, + ) end context "when enabled" do let(:private_key) { OpenSSL::PKey::RSA.generate(2048) } - let(:group_response) { - { - groups: [ - { - id: "12345", - name: "group1" - }, - { - id: "67890", - name: "group2" - } - ] - } - } + let(:group_response) do + { groups: [{ id: "12345", name: "group1" }, { id: "67890", name: "group2" }] } + end before do SiteSetting.google_oauth2_hd_groups_service_account_admin_email = "admin@example.com" SiteSetting.google_oauth2_hd_groups_service_account_json = { "private_key" => private_key.to_s, - "client_email": "discourse-group-sync@example.iam.gserviceaccount.com", + :"client_email" => "discourse-group-sync@example.iam.gserviceaccount.com", }.to_json SiteSetting.google_oauth2_hd_groups = true @@ -166,28 +160,30 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do stub_request(:post, "https://oauth2.googleapis.com/token").to_return do |request| jwt = Rack::Utils.parse_query(request.body)["assertion"] - decoded_token = JWT.decode(jwt, private_key.public_key, true, { algorithm: 'RS256' }) + decoded_token = JWT.decode(jwt, private_key.public_key, true, { algorithm: "RS256" }) { status: 200, body: { "access_token" => token, "type" => "bearer" }.to_json, - headers: { "Content-Type" => "application/json" } + headers: { + "Content-Type" => "application/json", + }, } rescue JWT::VerificationError - { - status: 403, - body: "Invalid JWT" - } + { status: 403, body: "Invalid JWT" } end - stub_request(:get, "https://admin.googleapis.com/admin/directory/v1/groups?userKey=#{@auth_hash.uid}"). - with(headers: { "Authorization" => "Bearer #{token}" }). - to_return do + stub_request( + :get, + "https://admin.googleapis.com/admin/directory/v1/groups?userKey=#{@auth_hash.uid}", + ) + .with(headers: { "Authorization" => "Bearer #{token}" }) + .to_return do { status: 200, body: group_response.to_json, headers: { - "Content-Type" => "application/json" - } + "Content-Type" => "application/json", + }, } end end @@ -206,7 +202,7 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do it "doesn't explode with invalid credentials" do SiteSetting.google_oauth2_hd_groups_service_account_json = { "private_key" => OpenSSL::PKey::RSA.generate(2048).to_s, - "client_email": "discourse-group-sync@example.iam.gserviceaccount.com", + :"client_email" => "discourse-group-sync@example.iam.gserviceaccount.com", }.to_json result = described_class.new.after_authenticate(@auth_hash) @@ -215,9 +211,7 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do end context "when disabled" do - before do - SiteSetting.google_oauth2_hd_groups = false - end + before { SiteSetting.google_oauth2_hd_groups = false } it "doesnt add associated groups" do result = described_class.new.after_authenticate(@auth_hash) @@ -227,16 +221,20 @@ RSpec.describe Auth::GoogleOAuth2Authenticator do end end - describe 'revoke' do + describe "revoke" do fab!(:user) { Fabricate(:user) } let(:authenticator) { Auth::GoogleOAuth2Authenticator.new } - it 'raises exception if no entry for user' do + it "raises exception if no entry for user" do expect { authenticator.revoke(user) }.to raise_error(Discourse::NotFound) end - it 'revokes correctly' do - UserAssociatedAccount.create!(provider_name: "google_oauth2", user_id: user.id, provider_uid: 12345) + it "revokes correctly" do + UserAssociatedAccount.create!( + provider_name: "google_oauth2", + user_id: user.id, + provider_uid: 12_345, + ) expect(authenticator.can_revoke?).to eq(true) expect(authenticator.revoke(user)).to eq(true) expect(authenticator.description_for_user(user)).to eq("") diff --git a/spec/lib/auth/managed_authenticator_spec.rb b/spec/lib/auth/managed_authenticator_spec.rb index a91cae99331..940be5776b1 100644 --- a/spec/lib/auth/managed_authenticator_spec.rb +++ b/spec/lib/auth/managed_authenticator_spec.rb @@ -1,19 +1,21 @@ # frozen_string_literal: true RSpec.describe Auth::ManagedAuthenticator do - let(:authenticator) { - Class.new(described_class) do - def name - "myauth" - end + let(:authenticator) do + Class + .new(described_class) do + def name + "myauth" + end - def primary_email_verified?(auth_token) - auth_token[:info][:email_verified] + def primary_email_verified?(auth_token) + auth_token[:info][:email_verified] + end end - end.new - } + .new + end - let(:hash) { + let(:hash) do OmniAuth::AuthHash.new( provider: "myauth", uid: "1234", @@ -21,25 +23,20 @@ RSpec.describe Auth::ManagedAuthenticator do name: "Best Display Name", email: "awesome@example.com", nickname: "IAmGroot", - email_verified: true + email_verified: true, }, credentials: { - token: "supersecrettoken" + token: "supersecrettoken", }, extra: { raw_info: { - randominfo: "some info" - } - } + randominfo: "some info", + }, + }, ) - } + end - let(:create_hash) { - OmniAuth::AuthHash.new( - provider: "myauth", - uid: "1234" - ) - } + let(:create_hash) { OmniAuth::AuthHash.new(provider: "myauth", uid: "1234") } def create_auth_result(attrs) auth_result = Auth::Result.new @@ -47,10 +44,16 @@ RSpec.describe Auth::ManagedAuthenticator do auth_result end - describe 'after_authenticate' do - it 'can match account from an existing association' do + describe "after_authenticate" do + it "can match account from an existing association" do user = Fabricate(:user) - associated = UserAssociatedAccount.create!(user: user, provider_name: 'myauth', provider_uid: "1234", last_used: 1.year.ago) + associated = + UserAssociatedAccount.create!( + user: user, + provider_name: "myauth", + provider_uid: "1234", + last_used: 1.year.ago, + ) result = authenticator.after_authenticate(hash) expect(result.user.id).to eq(user.id) @@ -62,36 +65,50 @@ RSpec.describe Auth::ManagedAuthenticator do expect(associated.extra["raw_info"]["randominfo"]).to eq("some info") end - it 'only sets email valid for present strings' do + it "only sets email valid for present strings" do # (Twitter sometimes sends empty email strings) - result = authenticator.after_authenticate(create_hash.merge(info: { email: "email@example.com", email_verified: true })) + result = + authenticator.after_authenticate( + create_hash.merge(info: { email: "email@example.com", email_verified: true }), + ) expect(result.email_valid).to eq(true) - result = authenticator.after_authenticate(create_hash.merge(info: { email: "", email_verified: true })) + result = + authenticator.after_authenticate( + create_hash.merge(info: { email: "", email_verified: true }), + ) expect(result.email_valid).to be_falsey - result = authenticator.after_authenticate(create_hash.merge(info: { email: nil, email_verified: true })) + result = + authenticator.after_authenticate( + create_hash.merge(info: { email: nil, email_verified: true }), + ) expect(result.email_valid).to be_falsey end - it 'does not set email valid if email_verified is false' do - result = authenticator.after_authenticate(create_hash.merge(info: { email: "email@example.com", email_verified: false })) + it "does not set email valid if email_verified is false" do + result = + authenticator.after_authenticate( + create_hash.merge(info: { email: "email@example.com", email_verified: false }), + ) expect(result.email_valid).to eq(false) end - describe 'connecting to another user account' do + describe "connecting to another user account" do fab!(:user1) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) } - before { UserAssociatedAccount.create!(user: user1, provider_name: 'myauth', provider_uid: "1234") } + before do + UserAssociatedAccount.create!(user: user1, provider_name: "myauth", provider_uid: "1234") + end - it 'works by default' do + it "works by default" do result = authenticator.after_authenticate(hash, existing_account: user2) expect(result.user.id).to eq(user2.id) expect(UserAssociatedAccount.exists?(user_id: user1.id)).to eq(false) expect(UserAssociatedAccount.exists?(user_id: user2.id)).to eq(true) end - it 'still works if another user has a matching email' do + it "still works if another user has a matching email" do Fabricate(:user, email: hash.dig(:info, :email)) result = authenticator.after_authenticate(hash, existing_account: user2) expect(result.user.id).to eq(user2.id) @@ -99,15 +116,18 @@ RSpec.describe Auth::ManagedAuthenticator do expect(UserAssociatedAccount.exists?(user_id: user2.id)).to eq(true) end - it 'does not work when disabled' do - authenticator = Class.new(described_class) do - def name - "myauth" - end - def can_connect_existing_user? - false - end - end.new + it "does not work when disabled" do + authenticator = + Class + .new(described_class) do + def name + "myauth" + end + def can_connect_existing_user? + false + end + end + .new result = authenticator.after_authenticate(hash, existing_account: user2) expect(result.user.id).to eq(user1.id) expect(UserAssociatedAccount.exists?(user_id: user1.id)).to eq(true) @@ -115,42 +135,48 @@ RSpec.describe Auth::ManagedAuthenticator do end end - describe 'match by email' do - it 'downcases the email address from the authprovider' do - result = authenticator.after_authenticate(hash.deep_merge(info: { email: "HELLO@example.com" })) - expect(result.email).to eq('hello@example.com') + describe "match by email" do + it "downcases the email address from the authprovider" do + result = + authenticator.after_authenticate(hash.deep_merge(info: { email: "HELLO@example.com" })) + expect(result.email).to eq("hello@example.com") end - it 'works normally' do + it "works normally" do user = Fabricate(:user) result = authenticator.after_authenticate(hash.deep_merge(info: { email: user.email })) expect(result.user.id).to eq(user.id) - expect(UserAssociatedAccount.find_by(provider_name: 'myauth', provider_uid: "1234").user_id).to eq(user.id) + expect( + UserAssociatedAccount.find_by(provider_name: "myauth", provider_uid: "1234").user_id, + ).to eq(user.id) end - it 'works if there is already an association with the target account' do + it "works if there is already an association with the target account" do user = Fabricate(:user, email: "awesome@example.com") result = authenticator.after_authenticate(hash) expect(result.user.id).to eq(user.id) end - it 'does not match if match_by_email is false' do - authenticator = Class.new(described_class) do - def name - "myauth" - end - def match_by_email - false - end - end.new + it "does not match if match_by_email is false" do + authenticator = + Class + .new(described_class) do + def name + "myauth" + end + def match_by_email + false + end + end + .new user = Fabricate(:user, email: "awesome@example.com") result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) end end - context 'when no matching user' do - it 'returns the correct information' do + context "when no matching user" do + it "returns the correct information" do expect { result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) @@ -161,13 +187,13 @@ RSpec.describe Auth::ManagedAuthenticator do expect(UserAssociatedAccount.last.info["nickname"]).to eq("IAmGroot") end - it 'works if there is already an association with the target account' do + it "works if there is already an association with the target account" do user = Fabricate(:user, email: "awesome@example.com") result = authenticator.after_authenticate(hash) expect(result.user.id).to eq(user.id) end - it 'works if there is no email' do + it "works if there is no email" do expect { result = authenticator.after_authenticate(hash.deep_merge(info: { email: nil })) expect(result.user).to eq(nil) @@ -178,7 +204,7 @@ RSpec.describe Auth::ManagedAuthenticator do expect(UserAssociatedAccount.last.info["nickname"]).to eq("IAmGroot") end - it 'will ignore name when equal to email' do + it "will ignore name when equal to email" do result = authenticator.after_authenticate(hash.deep_merge(info: { name: hash.info.email })) expect(result.email).to eq(hash.info.email) expect(result.name).to eq(nil) @@ -187,39 +213,61 @@ RSpec.describe Auth::ManagedAuthenticator do describe "avatar on update" do fab!(:user) { Fabricate(:user) } - let!(:associated) { UserAssociatedAccount.create!(user: user, provider_name: 'myauth', provider_uid: "1234") } + let!(:associated) do + UserAssociatedAccount.create!(user: user, provider_name: "myauth", provider_uid: "1234") + end it "schedules the job upon update correctly" do # No image supplied, do not schedule - expect { result = authenticator.after_authenticate(hash) } - .not_to change { Jobs::DownloadAvatarFromUrl.jobs.count } + expect { result = authenticator.after_authenticate(hash) }.not_to change { + Jobs::DownloadAvatarFromUrl.jobs.count + } # Image supplied, schedule - expect { result = authenticator.after_authenticate(hash.deep_merge(info: { image: "https://some.domain/image.jpg" })) } - .to change { Jobs::DownloadAvatarFromUrl.jobs.count }.by(1) + expect { + result = + authenticator.after_authenticate( + hash.deep_merge(info: { image: "https://some.domain/image.jpg" }), + ) + }.to change { Jobs::DownloadAvatarFromUrl.jobs.count }.by(1) # User already has profile picture, don't schedule user.user_avatar = Fabricate(:user_avatar, custom_upload: Fabricate(:upload)) user.save! - expect { result = authenticator.after_authenticate(hash.deep_merge(info: { image: "https://some.domain/image.jpg" })) } - .not_to change { Jobs::DownloadAvatarFromUrl.jobs.count } + expect { + result = + authenticator.after_authenticate( + hash.deep_merge(info: { image: "https://some.domain/image.jpg" }), + ) + }.not_to change { Jobs::DownloadAvatarFromUrl.jobs.count } end end describe "profile on update" do fab!(:user) { Fabricate(:user) } - let!(:associated) { UserAssociatedAccount.create!(user: user, provider_name: 'myauth', provider_uid: "1234") } + let!(:associated) do + UserAssociatedAccount.create!(user: user, provider_name: "myauth", provider_uid: "1234") + end it "updates the user's location and bio, unless already set" do { description: :bio_raw, location: :location }.each do |auth_hash_key, profile_key| user.user_profile.update(profile_key => "Initial Value") # No value supplied, do not overwrite - expect { result = authenticator.after_authenticate(hash) } - .not_to change { user.user_profile.reload; user.user_profile[profile_key] } + expect { result = authenticator.after_authenticate(hash) }.not_to change { + user.user_profile.reload + user.user_profile[profile_key] + } # Value supplied, still do not overwrite - expect { result = authenticator.after_authenticate(hash.deep_merge(info: { auth_hash_key => "New Value" })) } - .not_to change { user.user_profile.reload; user.user_profile[profile_key] } + expect { + result = + authenticator.after_authenticate( + hash.deep_merge(info: { auth_hash_key => "New Value" }), + ) + }.not_to change { + user.user_profile.reload + user.user_profile[profile_key] + } # User has not set a value, so overwrite user.user_profile.update(profile_key => "") @@ -232,24 +280,32 @@ RSpec.describe Auth::ManagedAuthenticator do describe "avatar on create" do fab!(:user) { Fabricate(:user) } - let!(:association) { UserAssociatedAccount.create!(provider_name: 'myauth', provider_uid: "1234") } + let!(:association) do + UserAssociatedAccount.create!(provider_name: "myauth", provider_uid: "1234") + end it "doesn't schedule with no image" do - expect { result = authenticator.after_create_account(user, create_auth_result(extra_data: create_hash)) } - .not_to change { Jobs::DownloadAvatarFromUrl.jobs.count } + expect { + result = + authenticator.after_create_account(user, create_auth_result(extra_data: create_hash)) + }.not_to change { Jobs::DownloadAvatarFromUrl.jobs.count } end it "schedules with image" do association.info["image"] = "https://some.domain/image.jpg" association.save! - expect { result = authenticator.after_create_account(user, create_auth_result(extra_data: create_hash)) } - .to change { Jobs::DownloadAvatarFromUrl.jobs.count }.by(1) + expect { + result = + authenticator.after_create_account(user, create_auth_result(extra_data: create_hash)) + }.to change { Jobs::DownloadAvatarFromUrl.jobs.count }.by(1) end end describe "profile on create" do fab!(:user) { Fabricate(:user) } - let!(:association) { UserAssociatedAccount.create!(provider_name: 'myauth', provider_uid: "1234") } + let!(:association) do + UserAssociatedAccount.create!(provider_name: "myauth", provider_uid: "1234") + end it "doesn't explode without profile" do authenticator.after_create_account(user, create_auth_result(extra_data: create_hash)) @@ -265,37 +321,44 @@ RSpec.describe Auth::ManagedAuthenticator do end end - describe 'match by username' do - let(:user_match_authenticator) { - Class.new(described_class) do - def name - "myauth" + describe "match by username" do + let(:user_match_authenticator) do + Class + .new(described_class) do + def name + "myauth" + end + def match_by_email + false + end + def match_by_username + true + end end - def match_by_email - false - end - def match_by_username - true - end - end.new - } - - it 'works normally' do - SiteSetting.username_change_period = 0 - user = Fabricate(:user) - result = user_match_authenticator.after_authenticate(hash.deep_merge(info: { nickname: user.username })) - expect(result.user.id).to eq(user.id) - expect(UserAssociatedAccount.find_by(provider_name: 'myauth', provider_uid: "1234").user_id).to eq(user.id) + .new end - it 'works if there is already an association with the target account' do + it "works normally" do + SiteSetting.username_change_period = 0 + user = Fabricate(:user) + result = + user_match_authenticator.after_authenticate( + hash.deep_merge(info: { nickname: user.username }), + ) + expect(result.user.id).to eq(user.id) + expect( + UserAssociatedAccount.find_by(provider_name: "myauth", provider_uid: "1234").user_id, + ).to eq(user.id) + end + + it "works if there is already an association with the target account" do SiteSetting.username_change_period = 0 user = Fabricate(:user, username: "IAmGroot") result = user_match_authenticator.after_authenticate(hash) expect(result.user.id).to eq(user.id) end - it 'works if the username is different case' do + it "works if the username is different case" do SiteSetting.username_change_period = 0 user = Fabricate(:user, username: "IAMGROOT") result = user_match_authenticator.after_authenticate(hash) @@ -309,28 +372,34 @@ RSpec.describe Auth::ManagedAuthenticator do expect(result.user).to eq(nil) end - it 'does not match if default match_by_username not overriden' do + it "does not match if default match_by_username not overriden" do SiteSetting.username_change_period = 0 - authenticator = Class.new(described_class) do - def name - "myauth" - end - end.new + authenticator = + Class + .new(described_class) do + def name + "myauth" + end + end + .new user = Fabricate(:user, username: "IAmGroot") result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) end - it 'does not match if match_by_username is false' do + it "does not match if match_by_username is false" do SiteSetting.username_change_period = 0 - authenticator = Class.new(described_class) do - def name - "myauth" - end - def match_by_username - false - end - end.new + authenticator = + Class + .new(described_class) do + def name + "myauth" + end + def match_by_username + false + end + end + .new user = Fabricate(:user, username: "IAmGroot") result = authenticator.after_authenticate(hash) expect(result.user).to eq(nil) @@ -338,38 +407,57 @@ RSpec.describe Auth::ManagedAuthenticator do end end - describe 'description_for_user' do + describe "description_for_user" do fab!(:user) { Fabricate(:user) } - it 'returns empty string if no entry for user' do + it "returns empty string if no entry for user" do expect(authenticator.description_for_user(user)).to eq("") end - it 'returns correct information' do - association = UserAssociatedAccount.create!(user: user, provider_name: 'myauth', provider_uid: "1234", info: { nickname: "somenickname", email: "test@domain.tld", name: "bestname" }) - expect(authenticator.description_for_user(user)).to eq('test@domain.tld') + it "returns correct information" do + association = + UserAssociatedAccount.create!( + user: user, + provider_name: "myauth", + provider_uid: "1234", + info: { + nickname: "somenickname", + email: "test@domain.tld", + name: "bestname", + }, + ) + expect(authenticator.description_for_user(user)).to eq("test@domain.tld") association.update(info: { nickname: "somenickname", name: "bestname" }) - expect(authenticator.description_for_user(user)).to eq('somenickname') + expect(authenticator.description_for_user(user)).to eq("somenickname") association.update(info: { nickname: "bestname" }) - expect(authenticator.description_for_user(user)).to eq('bestname') + expect(authenticator.description_for_user(user)).to eq("bestname") association.update(info: {}) - expect(authenticator.description_for_user(user)).to eq(I18n.t("associated_accounts.connected")) + expect(authenticator.description_for_user(user)).to eq( + I18n.t("associated_accounts.connected"), + ) end end - describe 'revoke' do + describe "revoke" do fab!(:user) { Fabricate(:user) } - it 'raises exception if no entry for user' do + it "raises exception if no entry for user" do expect { authenticator.revoke(user) }.to raise_error(Discourse::NotFound) end context "with valid record" do before do - UserAssociatedAccount.create!(user: user, provider_name: 'myauth', provider_uid: "1234", info: { name: "somename" }) + UserAssociatedAccount.create!( + user: user, + provider_name: "myauth", + provider_uid: "1234", + info: { + name: "somename", + }, + ) end - it 'revokes correctly' do + it "revokes correctly" do expect(authenticator.description_for_user(user)).to eq("somename") expect(authenticator.can_revoke?).to eq(true) expect(authenticator.revoke(user)).to eq(true) @@ -378,5 +466,4 @@ RSpec.describe Auth::ManagedAuthenticator do end end end - end diff --git a/spec/lib/auth/result_spec.rb b/spec/lib/auth/result_spec.rb index 2c9a83e0572..ebc5830ce7c 100644 --- a/spec/lib/auth/result_spec.rb +++ b/spec/lib/auth/result_spec.rb @@ -3,7 +3,9 @@ RSpec.describe Auth::Result do fab!(:initial_email) { "initialemail@example.org" } fab!(:initial_username) { "initialusername" } fab!(:initial_name) { "Initial Name" } - fab!(:user) { Fabricate(:user, email: initial_email, username: initial_username, name: initial_name) } + fab!(:user) do + Fabricate(:user, email: initial_email, username: initial_username, name: initial_name) + end let(:new_email) { "newemail@example.org" } let(:new_username) { "newusername" } diff --git a/spec/lib/auth/twitter_authenticator_spec.rb b/spec/lib/auth/twitter_authenticator_spec.rb index 14f5c834a6a..c6e1bdfd696 100644 --- a/spec/lib/auth/twitter_authenticator_spec.rb +++ b/spec/lib/auth/twitter_authenticator_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Auth::TwitterAuthenticator do nickname: "minion", }, uid: "123", - provider: "twitter" + provider: "twitter", } result = auth.after_authenticate(auth_token) @@ -25,7 +25,7 @@ RSpec.describe Auth::TwitterAuthenticator do expect(info.info["email"]).to eq(user.email) end - it 'can connect to a different existing user account' do + it "can connect to a different existing user account" do authenticator = Auth::TwitterAuthenticator.new user1 = Fabricate(:user) user2 = Fabricate(:user) @@ -40,7 +40,7 @@ RSpec.describe Auth::TwitterAuthenticator do nickname: "minion", }, uid: "100", - provider: "twitter" + provider: "twitter", } result = authenticator.after_authenticate(hash, existing_account: user2) @@ -50,19 +50,19 @@ RSpec.describe Auth::TwitterAuthenticator do expect(UserAssociatedAccount.exists?(provider_name: "twitter", user_id: user2.id)).to eq(true) end - describe 'revoke' do + describe "revoke" do fab!(:user) { Fabricate(:user) } let(:authenticator) { Auth::TwitterAuthenticator.new } - it 'raises exception if no entry for user' do + it "raises exception if no entry for user" do expect { authenticator.revoke(user) }.to raise_error(Discourse::NotFound) end - it 'revokes correctly' do - UserAssociatedAccount.create!(provider_name: "twitter", user_id: user.id, provider_uid: 100) - expect(authenticator.can_revoke?).to eq(true) - expect(authenticator.revoke(user)).to eq(true) - expect(authenticator.description_for_user(user)).to eq("") - end + it "revokes correctly" do + UserAssociatedAccount.create!(provider_name: "twitter", user_id: user.id, provider_uid: 100) + expect(authenticator.can_revoke?).to eq(true) + expect(authenticator.revoke(user)).to eq(true) + expect(authenticator.description_for_user(user)).to eq("") + end end end diff --git a/spec/lib/backup_restore/backup_file_handler_multisite_spec.rb b/spec/lib/backup_restore/backup_file_handler_multisite_spec.rb index 5a0b1f6de1c..40a01e26bca 100644 --- a/spec/lib/backup_restore/backup_file_handler_multisite_spec.rb +++ b/spec/lib/backup_restore/backup_file_handler_multisite_spec.rb @@ -10,7 +10,7 @@ RSpec.describe BackupRestore::BackupFileHandler, type: :multisite do expect_decompress_and_clean_up_to_work( backup_filename: "backup_till_v1.5.tar.gz", require_metadata_file: true, - require_uploads: true + require_uploads: true, ) end end diff --git a/spec/lib/backup_restore/backup_file_handler_spec.rb b/spec/lib/backup_restore/backup_file_handler_spec.rb index 8803de78908..c7f017692ed 100644 --- a/spec/lib/backup_restore/backup_file_handler_spec.rb +++ b/spec/lib/backup_restore/backup_file_handler_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative 'shared_context_for_backup_restore' +require_relative "shared_context_for_backup_restore" RSpec.describe BackupRestore::BackupFileHandler do include_context "with shared stuff" @@ -9,7 +9,7 @@ RSpec.describe BackupRestore::BackupFileHandler do expect_decompress_and_clean_up_to_work( backup_filename: "backup_since_v1.6.tar.gz", require_metadata_file: false, - require_uploads: true + require_uploads: true, ) end @@ -18,7 +18,7 @@ RSpec.describe BackupRestore::BackupFileHandler do backup_filename: "sql_only_backup.sql.gz", expected_dump_filename: "sql_only_backup.sql", require_metadata_file: false, - require_uploads: false + require_uploads: false, ) end @@ -27,11 +27,11 @@ RSpec.describe BackupRestore::BackupFileHandler do backup_filename: "backup_with_wrong_upload_path.tar.gz", require_metadata_file: false, require_uploads: true, - expected_upload_paths: [ - "uploads/default/original/1X/both.txt", - "uploads/default/original/1X/only-uploads.txt", - "uploads/default/original/1X/only-var.txt" - ] + expected_upload_paths: %w[ + uploads/default/original/1X/both.txt + uploads/default/original/1X/only-uploads.txt + uploads/default/original/1X/only-var.txt + ], ) do |upload_path| content = File.read(upload_path).chomp @@ -54,7 +54,7 @@ RSpec.describe BackupRestore::BackupFileHandler do backup_filename: "backup_since_v1.6.tar.gz", require_metadata_file: false, require_uploads: true, - location: BackupLocationSiteSetting::LOCAL + location: BackupLocationSiteSetting::LOCAL, ) end end diff --git a/spec/lib/backup_restore/backuper_spec.rb b/spec/lib/backup_restore/backuper_spec.rb index c66006d6313..1578a44f446 100644 --- a/spec/lib/backup_restore/backuper_spec.rb +++ b/spec/lib/backup_restore/backuper_spec.rb @@ -1,55 +1,55 @@ # frozen_string_literal: true RSpec.describe BackupRestore::Backuper do - it 'returns a non-empty parameterized title when site title contains unicode' do - SiteSetting.title = 'Ɣ' + it "returns a non-empty parameterized title when site title contains unicode" do + SiteSetting.title = "Ɣ" backuper = BackupRestore::Backuper.new(Discourse.system_user.id) expect(backuper.send(:get_parameterized_title)).to eq("discourse") end - it 'returns a valid parameterized site title' do + it "returns a valid parameterized site title" do SiteSetting.title = "Coding Horror" backuper = BackupRestore::Backuper.new(Discourse.system_user.id) expect(backuper.send(:get_parameterized_title)).to eq("coding-horror") end - describe '#notify_user' do - before do - freeze_time Time.zone.parse('2010-01-01 12:00') - end + describe "#notify_user" do + before { freeze_time Time.zone.parse("2010-01-01 12:00") } - it 'includes logs if short' do + it "includes logs if short" do SiteSetting.max_export_file_size_kb = 1 SiteSetting.export_authorized_extensions = "tar.gz" silence_stdout do backuper = BackupRestore::Backuper.new(Discourse.system_user.id) - expect { backuper.send(:notify_user) } - .to change { Topic.private_messages.count }.by(1) - .and not_change { Upload.count } + expect { backuper.send(:notify_user) }.to change { Topic.private_messages.count }.by( + 1, + ).and not_change { Upload.count } end - expect(Topic.last.first_post.raw).to include("```text\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```") + expect(Topic.last.first_post.raw).to include( + "```text\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```", + ) end - it 'include upload if log is long' do + it "include upload if log is long" do SiteSetting.max_post_length = 250 silence_stdout do backuper = BackupRestore::Backuper.new(Discourse.system_user.id) - expect { backuper.send(:notify_user) } - .to change { Topic.private_messages.count }.by(1) - .and change { Upload.where(original_filename: "log.txt.zip").count }.by(1) + expect { backuper.send(:notify_user) }.to change { Topic.private_messages.count }.by( + 1, + ).and change { Upload.where(original_filename: "log.txt.zip").count }.by(1) end expect(Topic.last.first_post.raw).to include("[log.txt.zip|attachment]") end - it 'includes trimmed logs if log is long and upload cannot be saved' do + it "includes trimmed logs if log is long and upload cannot be saved" do SiteSetting.max_post_length = 348 SiteSetting.max_export_file_size_kb = 1 SiteSetting.export_authorized_extensions = "tar.gz" @@ -57,16 +57,16 @@ RSpec.describe BackupRestore::Backuper do silence_stdout do backuper = BackupRestore::Backuper.new(Discourse.system_user.id) - 1.upto(10).each do |i| - backuper.send(:log, "Line #{i}") - end + 1.upto(10).each { |i| backuper.send(:log, "Line #{i}") } - expect { backuper.send(:notify_user) } - .to change { Topic.private_messages.count }.by(1) - .and not_change { Upload.count } + expect { backuper.send(:notify_user) }.to change { Topic.private_messages.count }.by( + 1, + ).and not_change { Upload.count } end - expect(Topic.last.first_post.raw).to include("```text\n...\n[2010-01-01 12:00:00] Line 10\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```") + expect(Topic.last.first_post.raw).to include( + "```text\n...\n[2010-01-01 12:00:00] Line 10\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```", + ) end end end diff --git a/spec/lib/backup_restore/database_restorer_spec.rb b/spec/lib/backup_restore/database_restorer_spec.rb index e8d69c0723c..afa4bea4f36 100644 --- a/spec/lib/backup_restore/database_restorer_spec.rb +++ b/spec/lib/backup_restore/database_restorer_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative 'shared_context_for_backup_restore' +require_relative "shared_context_for_backup_restore" RSpec.describe BackupRestore::DatabaseRestorer do include_context "with shared stuff" @@ -32,7 +32,7 @@ RSpec.describe BackupRestore::DatabaseRestorer do context "with real psql" do after do psql = BackupRestore::DatabaseRestorer.psql_command - system("#{psql} -c 'DROP TABLE IF EXISTS foo'", [:out, :err] => File::NULL) + system("#{psql} -c 'DROP TABLE IF EXISTS foo'", %i[out err] => File::NULL) end def restore(filename, stub_migrate: true) @@ -74,24 +74,27 @@ RSpec.describe BackupRestore::DatabaseRestorer do end it "detects error during restore" do - expect { restore("error.sql", stub_migrate: false) } - .to raise_error(BackupRestore::DatabaseRestoreError) + expect { restore("error.sql", stub_migrate: false) }.to raise_error( + BackupRestore::DatabaseRestoreError, + ) end end describe "rewrites database dump" do let(:logger) do - Class.new do - attr_reader :log_messages + Class + .new do + attr_reader :log_messages - def initialize - @log_messages = [] - end + def initialize + @log_messages = [] + end - def log(message, ex = nil) - @log_messages << message if message + def log(message, ex = nil) + @log_messages << message if message + end end - end.new + .new end def restore_and_log_output(filename) @@ -108,8 +111,12 @@ RSpec.describe BackupRestore::DatabaseRestorer do expect(log).not_to be_blank expect(log).not_to match(/CREATE SCHEMA public/) expect(log).not_to match(/EXECUTE FUNCTION/) - expect(log).to match(/^CREATE TRIGGER foo_topic_id_readonly .+? EXECUTE PROCEDURE discourse_functions.raise_foo_topic_id_readonly/) - expect(log).to match(/^CREATE TRIGGER foo_user_id_readonly .+? EXECUTE PROCEDURE discourse_functions.raise_foo_user_id_readonly/) + expect(log).to match( + /^CREATE TRIGGER foo_topic_id_readonly .+? EXECUTE PROCEDURE discourse_functions.raise_foo_topic_id_readonly/, + ) + expect(log).to match( + /^CREATE TRIGGER foo_user_id_readonly .+? EXECUTE PROCEDURE discourse_functions.raise_foo_user_id_readonly/, + ) end it "does not replace `EXECUTE FUNCTION` when restoring on PostgreSQL >= 11" do @@ -119,13 +126,17 @@ RSpec.describe BackupRestore::DatabaseRestorer do expect(log).not_to be_blank expect(log).not_to match(/CREATE SCHEMA public/) expect(log).not_to match(/EXECUTE PROCEDURE/) - expect(log).to match(/^CREATE TRIGGER foo_topic_id_readonly .+? EXECUTE FUNCTION discourse_functions.raise_foo_topic_id_readonly/) - expect(log).to match(/^CREATE TRIGGER foo_user_id_readonly .+? EXECUTE FUNCTION discourse_functions.raise_foo_user_id_readonly/) + expect(log).to match( + /^CREATE TRIGGER foo_topic_id_readonly .+? EXECUTE FUNCTION discourse_functions.raise_foo_topic_id_readonly/, + ) + expect(log).to match( + /^CREATE TRIGGER foo_user_id_readonly .+? EXECUTE FUNCTION discourse_functions.raise_foo_user_id_readonly/, + ) end end describe "database connection" do - it 'it is not erroring for non-multisite' do + it "it is not erroring for non-multisite" do expect { execute_stubbed_restore }.not_to raise_error end end @@ -147,7 +158,7 @@ RSpec.describe BackupRestore::DatabaseRestorer do describe "readonly functions" do before do BackupRestore::DatabaseRestorer.stubs(:core_migration_files).returns( - Dir[Rails.root.join("spec/fixtures/db/post_migrate/drop_column/**/*.rb")] + Dir[Rails.root.join("spec/fixtures/db/post_migrate/drop_column/**/*.rb")], ) end @@ -167,8 +178,9 @@ RSpec.describe BackupRestore::DatabaseRestorer do end it "creates and drops only missing functions during restore" do - Migration::BaseDropper.stubs(:existing_discourse_function_names) - .returns(%w(raise_email_logs_readonly raise_posts_raw_email_readonly)) + Migration::BaseDropper.stubs(:existing_discourse_function_names).returns( + %w[raise_email_logs_readonly raise_posts_raw_email_readonly], + ) Migration::BaseDropper.expects(:create_readonly_function).with(:posts, :via_email) execute_stubbed_restore(stub_readonly_functions: false) @@ -191,9 +203,7 @@ RSpec.describe BackupRestore::DatabaseRestorer do end context "when a backup schema exists" do - before do - ActiveRecord::Base.connection.expects(:schema_exists?).with("backup").returns(true) - end + before { ActiveRecord::Base.connection.expects(:schema_exists?).with("backup").returns(true) } it "drops the schema when the last restore was long ago" do ActiveRecord::Base.connection.expects(:drop_schema).with("backup") diff --git a/spec/lib/backup_restore/local_backup_store_spec.rb b/spec/lib/backup_restore/local_backup_store_spec.rb index faa01c4f165..345fb4e4d16 100644 --- a/spec/lib/backup_restore/local_backup_store_spec.rb +++ b/spec/lib/backup_restore/local_backup_store_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'backup_restore/local_backup_store' -require_relative 'shared_examples_for_backup_store' +require "backup_restore/local_backup_store" +require_relative "shared_examples_for_backup_store" RSpec.describe BackupRestore::LocalBackupStore do before do @@ -10,9 +10,7 @@ RSpec.describe BackupRestore::LocalBackupStore do SiteSetting.backup_location = BackupLocationSiteSetting::LOCAL end - after do - FileUtils.remove_dir(@root_directory, true) - end + after { FileUtils.remove_dir(@root_directory, true) } subject(:store) { BackupRestore::BackupStore.create(root_directory: @root_directory) } let(:expected_type) { BackupRestore::LocalBackupStore } @@ -24,14 +22,49 @@ RSpec.describe BackupRestore::LocalBackupStore do end def create_backups - create_file(db_name: "default", filename: "b.tar.gz", last_modified: "2018-09-13T15:10:00Z", size_in_bytes: 17) - create_file(db_name: "default", filename: "a.tgz", last_modified: "2018-02-11T09:27:00Z", size_in_bytes: 29) - create_file(db_name: "default", filename: "r.sql.gz", last_modified: "2017-12-20T03:48:00Z", size_in_bytes: 11) - create_file(db_name: "default", filename: "no-backup.txt", last_modified: "2018-09-05T14:27:00Z", size_in_bytes: 12) - create_file(db_name: "default/subfolder", filename: "c.tar.gz", last_modified: "2019-01-24T18:44:00Z", size_in_bytes: 23) + create_file( + db_name: "default", + filename: "b.tar.gz", + last_modified: "2018-09-13T15:10:00Z", + size_in_bytes: 17, + ) + create_file( + db_name: "default", + filename: "a.tgz", + last_modified: "2018-02-11T09:27:00Z", + size_in_bytes: 29, + ) + create_file( + db_name: "default", + filename: "r.sql.gz", + last_modified: "2017-12-20T03:48:00Z", + size_in_bytes: 11, + ) + create_file( + db_name: "default", + filename: "no-backup.txt", + last_modified: "2018-09-05T14:27:00Z", + size_in_bytes: 12, + ) + create_file( + db_name: "default/subfolder", + filename: "c.tar.gz", + last_modified: "2019-01-24T18:44:00Z", + size_in_bytes: 23, + ) - create_file(db_name: "second", filename: "multi-2.tar.gz", last_modified: "2018-11-27T03:16:54Z", size_in_bytes: 19) - create_file(db_name: "second", filename: "multi-1.tar.gz", last_modified: "2018-11-26T03:17:09Z", size_in_bytes: 22) + create_file( + db_name: "second", + filename: "multi-2.tar.gz", + last_modified: "2018-11-27T03:16:54Z", + size_in_bytes: 19, + ) + create_file( + db_name: "second", + filename: "multi-1.tar.gz", + last_modified: "2018-11-26T03:17:09Z", + size_in_bytes: 22, + ) end def remove_backups diff --git a/spec/lib/backup_restore/meta_data_handler_spec.rb b/spec/lib/backup_restore/meta_data_handler_spec.rb index 9dd08f0e822..70f062e89d3 100644 --- a/spec/lib/backup_restore/meta_data_handler_spec.rb +++ b/spec/lib/backup_restore/meta_data_handler_spec.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require_relative 'shared_context_for_backup_restore' +require_relative "shared_context_for_backup_restore" RSpec.describe BackupRestore::MetaDataHandler do include_context "with shared stuff" - let!(:backup_filename) { 'discourse-2019-11-18-143242-v20191108000414.tar.gz' } + let!(:backup_filename) { "discourse-2019-11-18-143242-v20191108000414.tar.gz" } def with_metadata_file(content) Dir.mktmpdir do |directory| @@ -27,8 +27,7 @@ RSpec.describe BackupRestore::MetaDataHandler do metadata = '{"source":"discourse","version":20160329101122}' with_metadata_file(metadata) do |dir| - expect(validate_metadata(backup_filename, dir)) - .to include(version: 20160329101122) + expect(validate_metadata(backup_filename, dir)).to include(version: 20_160_329_101_122) end end @@ -36,15 +35,17 @@ RSpec.describe BackupRestore::MetaDataHandler do corrupt_metadata = '{"version":20160329101122' with_metadata_file(corrupt_metadata) do |dir| - expect { validate_metadata(backup_filename, dir) } - .to raise_error(BackupRestore::MetaDataError) + expect { validate_metadata(backup_filename, dir) }.to raise_error( + BackupRestore::MetaDataError, + ) end end it "raises an exception when the metadata file is empty" do - with_metadata_file('') do |dir| - expect { validate_metadata(backup_filename, dir) } - .to raise_error(BackupRestore::MetaDataError) + with_metadata_file("") do |dir| + expect { validate_metadata(backup_filename, dir) }.to raise_error( + BackupRestore::MetaDataError, + ) end end @@ -52,8 +53,9 @@ RSpec.describe BackupRestore::MetaDataHandler do metadata = '{"source":"discourse","version":"1abcdefghijklm"}' with_metadata_file(metadata) do |dir| - expect { validate_metadata(backup_filename, dir) } - .to raise_error(BackupRestore::MetaDataError) + expect { validate_metadata(backup_filename, dir) }.to raise_error( + BackupRestore::MetaDataError, + ) end end @@ -61,8 +63,9 @@ RSpec.describe BackupRestore::MetaDataHandler do metadata = '{"source":"discourse","version":""}' with_metadata_file(metadata) do |dir| - expect { validate_metadata(backup_filename, dir) } - .to raise_error(BackupRestore::MetaDataError) + expect { validate_metadata(backup_filename, dir) }.to raise_error( + BackupRestore::MetaDataError, + ) end end end @@ -70,36 +73,32 @@ RSpec.describe BackupRestore::MetaDataHandler do describe "filename" do it "extracts metadata from filename when metadata file does not exist" do with_metadata_file(nil) do |dir| - expect(validate_metadata(backup_filename, dir)) - .to include(version: 20191108000414) + expect(validate_metadata(backup_filename, dir)).to include(version: 20_191_108_000_414) end end it "raises an exception when the filename contains no version number" do - filename = 'discourse-2019-11-18-143242.tar.gz' + filename = "discourse-2019-11-18-143242.tar.gz" - expect { validate_metadata(filename, nil) } - .to raise_error(BackupRestore::MetaDataError) + expect { validate_metadata(filename, nil) }.to raise_error(BackupRestore::MetaDataError) end it "raises an exception when the filename contains an invalid version number" do - filename = 'discourse-2019-11-18-143242-v123456789.tar.gz' - expect { validate_metadata(filename, nil) } - .to raise_error(BackupRestore::MetaDataError) + filename = "discourse-2019-11-18-143242-v123456789.tar.gz" + expect { validate_metadata(filename, nil) }.to raise_error(BackupRestore::MetaDataError) - filename = 'discourse-2019-11-18-143242-v1abcdefghijklm.tar.gz' - expect { validate_metadata(filename, nil) } - .to raise_error(BackupRestore::MetaDataError) + filename = "discourse-2019-11-18-143242-v1abcdefghijklm.tar.gz" + expect { validate_metadata(filename, nil) }.to raise_error(BackupRestore::MetaDataError) end end it "raises an exception when the backup's version is newer than the current version" do - new_backup_filename = 'discourse-2019-11-18-143242-v20191113193141.sql.gz' + new_backup_filename = "discourse-2019-11-18-143242-v20191113193141.sql.gz" - BackupRestore.expects(:current_version) - .returns(20191025005204).once + BackupRestore.expects(:current_version).returns(20_191_025_005_204).once - expect { validate_metadata(new_backup_filename, nil) } - .to raise_error(BackupRestore::MigrationRequiredError) + expect { validate_metadata(new_backup_filename, nil) }.to raise_error( + BackupRestore::MigrationRequiredError, + ) end end diff --git a/spec/lib/backup_restore/s3_backup_store_spec.rb b/spec/lib/backup_restore/s3_backup_store_spec.rb index 4aad803cfd4..7989fbaa4d8 100644 --- a/spec/lib/backup_restore/s3_backup_store_spec.rb +++ b/spec/lib/backup_restore/s3_backup_store_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 's3_helper' -require 'backup_restore/s3_backup_store' -require_relative 'shared_examples_for_backup_store' +require "s3_helper" +require "backup_restore/s3_backup_store" +require_relative "shared_examples_for_backup_store" RSpec.describe BackupRestore::S3BackupStore do before do @@ -21,49 +21,64 @@ RSpec.describe BackupRestore::S3BackupStore do expect(context.params[:prefix]).to eq(expected_prefix) if context.params.key?(:prefix) end - @s3_client.stub_responses(:list_objects_v2, -> (context) do - check_context(context) + @s3_client.stub_responses( + :list_objects_v2, + ->(context) { + check_context(context) - { contents: objects_with_prefix(context) } - end) + { contents: objects_with_prefix(context) } + }, + ) - @s3_client.stub_responses(:delete_object, -> (context) do - check_context(context) + @s3_client.stub_responses( + :delete_object, + ->(context) { + check_context(context) - expect do - @objects.delete_if { |obj| obj[:key] == context.params[:key] } - end.to change { @objects } - end) + expect do @objects.delete_if { |obj| obj[:key] == context.params[:key] } end.to change { + @objects + } + }, + ) - @s3_client.stub_responses(:head_object, -> (context) do - check_context(context) + @s3_client.stub_responses( + :head_object, + ->(context) { + check_context(context) - if object = @objects.find { |obj| obj[:key] == context.params[:key] } - { content_length: object[:size], last_modified: object[:last_modified] } - else - { status_code: 404, headers: {}, body: "", } - end - end) + if object = @objects.find { |obj| obj[:key] == context.params[:key] } + { content_length: object[:size], last_modified: object[:last_modified] } + else + { status_code: 404, headers: {}, body: "" } + end + }, + ) - @s3_client.stub_responses(:get_object, -> (context) do - check_context(context) + @s3_client.stub_responses( + :get_object, + ->(context) { + check_context(context) - if object = @objects.find { |obj| obj[:key] == context.params[:key] } - { content_length: object[:size], body: "A" * object[:size] } - else - { status_code: 404, headers: {}, body: "", } - end - end) + if object = @objects.find { |obj| obj[:key] == context.params[:key] } + { content_length: object[:size], body: "A" * object[:size] } + else + { status_code: 404, headers: {}, body: "" } + end + }, + ) - @s3_client.stub_responses(:put_object, -> (context) do - check_context(context) + @s3_client.stub_responses( + :put_object, + ->(context) { + check_context(context) - @objects << { - key: context.params[:key], - size: context.params[:body].size, - last_modified: Time.zone.now - } - end) + @objects << { + key: context.params[:key], + size: context.params[:body].size, + last_modified: Time.zone.now, + } + }, + ) SiteSetting.s3_backup_bucket = "s3-backup-bucket" SiteSetting.s3_access_key_id = "s3-access-key-id" @@ -105,15 +120,47 @@ RSpec.describe BackupRestore::S3BackupStore do def create_backups @objects.clear - @objects << { key: "default/b.tar.gz", size: 17, last_modified: Time.parse("2018-09-13T15:10:00Z") } - @objects << { key: "default/a.tgz", size: 29, last_modified: Time.parse("2018-02-11T09:27:00Z") } - @objects << { key: "default/r.sql.gz", size: 11, last_modified: Time.parse("2017-12-20T03:48:00Z") } - @objects << { key: "default/no-backup.txt", size: 12, last_modified: Time.parse("2018-09-05T14:27:00Z") } - @objects << { key: "default/subfolder/c.tar.gz", size: 23, last_modified: Time.parse("2019-01-24T18:44:00Z") } + @objects << { + key: "default/b.tar.gz", + size: 17, + last_modified: Time.parse("2018-09-13T15:10:00Z"), + } + @objects << { + key: "default/a.tgz", + size: 29, + last_modified: Time.parse("2018-02-11T09:27:00Z"), + } + @objects << { + key: "default/r.sql.gz", + size: 11, + last_modified: Time.parse("2017-12-20T03:48:00Z"), + } + @objects << { + key: "default/no-backup.txt", + size: 12, + last_modified: Time.parse("2018-09-05T14:27:00Z"), + } + @objects << { + key: "default/subfolder/c.tar.gz", + size: 23, + last_modified: Time.parse("2019-01-24T18:44:00Z"), + } - @objects << { key: "second/multi-2.tar.gz", size: 19, last_modified: Time.parse("2018-11-27T03:16:54Z") } - @objects << { key: "second/multi-1.tar.gz", size: 22, last_modified: Time.parse("2018-11-26T03:17:09Z") } - @objects << { key: "second/subfolder/multi-3.tar.gz", size: 23, last_modified: Time.parse("2019-01-24T18:44:00Z") } + @objects << { + key: "second/multi-2.tar.gz", + size: 19, + last_modified: Time.parse("2018-11-27T03:16:54Z"), + } + @objects << { + key: "second/multi-1.tar.gz", + size: 22, + last_modified: Time.parse("2018-11-26T03:17:09Z"), + } + @objects << { + key: "second/subfolder/multi-3.tar.gz", + size: 23, + last_modified: Time.parse("2019-01-24T18:44:00Z"), + } end def remove_backups @@ -126,7 +173,7 @@ RSpec.describe BackupRestore::S3BackupStore do filename = Regexp.escape(filename) expires = SiteSetting.s3_presigned_get_url_expires_after_seconds - /\Ahttps:\/\/#{bucket}.*#{prefix}\/#{filename}\?.*X-Amz-Expires=#{expires}.*X-Amz-Signature=.*\z/ + %r{\Ahttps://#{bucket}.*#{prefix}/#{filename}\?.*X-Amz-Expires=#{expires}.*X-Amz-Signature=.*\z} end def upload_url_regex(db_name, filename, multisite:) @@ -135,7 +182,7 @@ RSpec.describe BackupRestore::S3BackupStore do filename = Regexp.escape(filename) expires = BackupRestore::S3BackupStore::UPLOAD_URL_EXPIRES_AFTER_SECONDS - /\Ahttps:\/\/#{bucket}.*#{prefix}\/#{filename}\?.*X-Amz-Expires=#{expires}.*X-Amz-Signature=.*\z/ + %r{\Ahttps://#{bucket}.*#{prefix}/#{filename}\?.*X-Amz-Expires=#{expires}.*X-Amz-Signature=.*\z} end def file_prefix(db_name, multisite) diff --git a/spec/lib/backup_restore/shared_context_for_backup_restore.rb b/spec/lib/backup_restore/shared_context_for_backup_restore.rb index 5ad59e604f1..402a7749939 100644 --- a/spec/lib/backup_restore/shared_context_for_backup_restore.rb +++ b/spec/lib/backup_restore/shared_context_for_backup_restore.rb @@ -2,9 +2,12 @@ RSpec.shared_context "with shared stuff" do let!(:logger) do - Class.new do - def log(message, ex = nil); end - end.new + Class + .new do + def log(message, ex = nil) + end + end + .new end def expect_create_readonly_functions @@ -33,13 +36,14 @@ RSpec.shared_context "with shared stuff" do end def expect_db_migrate - Discourse::Utils.expects(:execute_command).with do |env, *command, options| - env["SKIP_POST_DEPLOYMENT_MIGRATIONS"] == "0" && - env["SKIP_OPTIMIZE_ICONS"] == "1" && - env["DISABLE_TRANSLATION_OVERRIDES"] == "1" && - command == ["rake", "db:migrate"] && - options[:chdir] == Rails.root - end.once + Discourse::Utils + .expects(:execute_command) + .with do |env, *command, options| + env["SKIP_POST_DEPLOYMENT_MIGRATIONS"] == "0" && env["SKIP_OPTIMIZE_ICONS"] == "1" && + env["DISABLE_TRANSLATION_OVERRIDES"] == "1" && command == %w[rake db:migrate] && + options[:chdir] == Rails.root + end + .once end def expect_db_reconnect @@ -76,11 +80,14 @@ RSpec.shared_context "with shared stuff" do Dir.mktmpdir do |root_directory| current_db = RailsMultisite::ConnectionManagement.current_db - file_handler = BackupRestore::BackupFileHandler.new( - logger, backup_filename, current_db, - root_tmp_directory: root_directory, - location: location - ) + file_handler = + BackupRestore::BackupFileHandler.new( + logger, + backup_filename, + current_db, + root_tmp_directory: root_directory, + location: location, + ) tmp_directory, db_dump_path = file_handler.decompress expected_tmp_path = File.join(root_directory, "tmp/restores", current_db, "2019-12-24-143148") @@ -93,11 +100,14 @@ RSpec.shared_context "with shared stuff" do expect(File.exist?(File.join(tmp_directory, "meta.json"))).to eq(require_metadata_file) if require_uploads - expected_upload_paths ||= ["uploads/default/original/3X/b/d/bd269860bb508aebcb6f08fe7289d5f117830383.png"] + expected_upload_paths ||= [ + "uploads/default/original/3X/b/d/bd269860bb508aebcb6f08fe7289d5f117830383.png", + ] expected_upload_paths.each do |upload_path| absolute_upload_path = File.join(tmp_directory, upload_path) - expect(File.exist?(absolute_upload_path)).to eq(true), "expected file #{upload_path} does not exist" + expect(File.exist?(absolute_upload_path)).to eq(true), + "expected file #{upload_path} does not exist" yield(absolute_upload_path) if block_given? end else @@ -112,6 +122,10 @@ RSpec.shared_context "with shared stuff" do # We don't want to delete the directory unless it is empty, otherwise this could be annoying # when tests run for the "default" database in a development environment. - FileUtils.rmdir(target_directory) rescue nil + begin + FileUtils.rmdir(target_directory) + rescue StandardError + nil + end end end diff --git a/spec/lib/backup_restore/shared_examples_for_backup_store.rb b/spec/lib/backup_restore/shared_examples_for_backup_store.rb index eeb8ee2e053..a35456a83cc 100644 --- a/spec/lib/backup_restore/shared_examples_for_backup_store.rb +++ b/spec/lib/backup_restore/shared_examples_for_backup_store.rb @@ -6,13 +6,39 @@ RSpec.shared_context "with backups" do after { remove_backups } # default backup files - let(:backup1) { BackupFile.new(filename: "b.tar.gz", size: 17, last_modified: Time.parse("2018-09-13T15:10:00Z")) } - let(:backup2) { BackupFile.new(filename: "a.tgz", size: 29, last_modified: Time.parse("2018-02-11T09:27:00Z")) } - let(:backup3) { BackupFile.new(filename: "r.sql.gz", size: 11, last_modified: Time.parse("2017-12-20T03:48:00Z")) } + let(:backup1) do + BackupFile.new( + filename: "b.tar.gz", + size: 17, + last_modified: Time.parse("2018-09-13T15:10:00Z"), + ) + end + let(:backup2) do + BackupFile.new(filename: "a.tgz", size: 29, last_modified: Time.parse("2018-02-11T09:27:00Z")) + end + let(:backup3) do + BackupFile.new( + filename: "r.sql.gz", + size: 11, + last_modified: Time.parse("2017-12-20T03:48:00Z"), + ) + end # backup files on another multisite - let(:backup4) { BackupFile.new(filename: "multi-1.tar.gz", size: 22, last_modified: Time.parse("2018-11-26T03:17:09Z")) } - let(:backup5) { BackupFile.new(filename: "multi-2.tar.gz", size: 19, last_modified: Time.parse("2018-11-27T03:16:54Z")) } + let(:backup4) do + BackupFile.new( + filename: "multi-1.tar.gz", + size: 22, + last_modified: Time.parse("2018-11-26T03:17:09Z"), + ) + end + let(:backup5) do + BackupFile.new( + filename: "multi-2.tar.gz", + size: 19, + last_modified: Time.parse("2018-11-27T03:16:54Z"), + ) + end end RSpec.shared_examples "backup store" do @@ -56,13 +82,15 @@ RSpec.shared_examples "backup store" do it "returns only *.gz and *.tgz files" do files = store.files expect(files).to_not be_empty - expect(files.map(&:filename)).to contain_exactly(backup1.filename, backup2.filename, backup3.filename) + expect(files.map(&:filename)).to contain_exactly( + backup1.filename, + backup2.filename, + backup3.filename, + ) end it "works with multisite", type: :multisite do - test_multisite_connection("second") do - expect(store.files).to eq([backup5, backup4]) - end + test_multisite_connection("second") { expect(store.files).to eq([backup5, backup4]) } end end @@ -77,9 +105,7 @@ RSpec.shared_examples "backup store" do end it "works with multisite", type: :multisite do - test_multisite_connection("second") do - expect(store.latest_file).to eq(backup5) - end + test_multisite_connection("second") { expect(store.latest_file).to eq(backup5) } end end @@ -222,11 +248,7 @@ RSpec.shared_examples "remote backup store" do # to that freeze_time(Time.now.to_s) - backup = BackupFile.new( - filename: "foo.tar.gz", - size: 33, - last_modified: Time.zone.now - ) + backup = BackupFile.new(filename: "foo.tar.gz", size: 33, last_modified: Time.zone.now) expect(store.files).to_not include(backup) @@ -246,15 +268,14 @@ RSpec.shared_examples "remote backup store" do end it "works with multisite", type: :multisite do - test_multisite_connection("second") do - upload_file - end + test_multisite_connection("second") { upload_file } end it "raises an exception when a file with same filename exists" do Tempfile.create(backup1.filename) do |file| - expect { store.upload_file(backup1.filename, file.path, "application/gzip") } - .to raise_exception(BackupRestore::BackupStore::BackupFileExists) + expect { + store.upload_file(backup1.filename, file.path, "application/gzip") + }.to raise_exception(BackupRestore::BackupStore::BackupFileExists) end end end @@ -268,8 +289,9 @@ RSpec.shared_examples "remote backup store" do end it "raises an exception when a file with same filename exists" do - expect { store.generate_upload_url(backup1.filename) } - .to raise_exception(BackupRestore::BackupStore::BackupFileExists) + expect { store.generate_upload_url(backup1.filename) }.to raise_exception( + BackupRestore::BackupStore::BackupFileExists, + ) end it "works with multisite", type: :multisite do diff --git a/spec/lib/backup_restore/system_interface_spec.rb b/spec/lib/backup_restore/system_interface_spec.rb index 20a701d8d32..8f1461cc17c 100644 --- a/spec/lib/backup_restore/system_interface_spec.rb +++ b/spec/lib/backup_restore/system_interface_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative 'shared_context_for_backup_restore' +require_relative "shared_context_for_backup_restore" RSpec.describe BackupRestore::SystemInterface do include_context "with shared stuff" @@ -8,9 +8,7 @@ RSpec.describe BackupRestore::SystemInterface do subject { BackupRestore::SystemInterface.new(logger) } describe "readonly mode" do - after do - Discourse::READONLY_KEYS.each { |key| Discourse.redis.del(key) } - end + after { Discourse::READONLY_KEYS.each { |key| Discourse.redis.del(key) } } describe "#enable_readonly_mode" do it "enables readonly mode" do @@ -107,45 +105,43 @@ RSpec.describe BackupRestore::SystemInterface do end context "with Sidekiq workers" do - after do - flush_sidekiq_redis_namespace - end + after { flush_sidekiq_redis_namespace } def flush_sidekiq_redis_namespace - Sidekiq.redis do |redis| - redis.scan_each { |key| redis.del(key) } - end + Sidekiq.redis { |redis| redis.scan_each { |key| redis.del(key) } } end def create_workers(site_id: nil, all_sites: false) - payload = Sidekiq::Testing.fake! do - data = { post_id: 1 } + payload = + Sidekiq::Testing.fake! do + data = { post_id: 1 } - if all_sites - data[:all_sites] = true - else - data[:current_site_id] = site_id || RailsMultisite::ConnectionManagement.current_db + if all_sites + data[:all_sites] = true + else + data[:current_site_id] = site_id || RailsMultisite::ConnectionManagement.current_db + end + + Jobs.enqueue(:process_post, data) + Jobs::ProcessPost.jobs.last end - Jobs.enqueue(:process_post, data) - Jobs::ProcessPost.jobs.last - end - Sidekiq.redis do |conn| hostname = "localhost" pid = 7890 key = "#{hostname}:#{pid}" process = { pid: pid, hostname: hostname } - conn.sadd('processes', key) - conn.hmset(key, 'info', Sidekiq.dump_json(process)) + conn.sadd("processes", key) + conn.hmset(key, "info", Sidekiq.dump_json(process)) - data = Sidekiq.dump_json( - queue: 'default', - run_at: Time.now.to_i, - payload: Sidekiq.dump_json(payload) - ) - conn.hmset("#{key}:work", '444', data) + data = + Sidekiq.dump_json( + queue: "default", + run_at: Time.now.to_i, + payload: Sidekiq.dump_json(payload), + ) + conn.hmset("#{key}:work", "444", data) end end diff --git a/spec/lib/backup_restore/uploads_restorer_spec.rb b/spec/lib/backup_restore/uploads_restorer_spec.rb index 3314358e9e4..ff00811c9aa 100644 --- a/spec/lib/backup_restore/uploads_restorer_spec.rb +++ b/spec/lib/backup_restore/uploads_restorer_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # rubocop:disable Discourse/OnlyTopLevelMultisiteSpecs -require_relative 'shared_context_for_backup_restore' +require_relative "shared_context_for_backup_restore" RSpec.describe BackupRestore::UploadsRestorer do include_context "with shared stuff" @@ -21,11 +21,19 @@ RSpec.describe BackupRestore::UploadsRestorer do expect_remaps( source_site_name: source_site_name, target_site_name: target_site_name, - metadata: metadata + metadata: metadata, ) end - def expect_remap(source_site_name: nil, target_site_name:, metadata: [], from:, to:, regex: false, &block) + def expect_remap( + source_site_name: nil, + target_site_name:, + metadata: [], + from:, + to:, + regex: false, + &block + ) expect_remaps( source_site_name: source_site_name, target_site_name: target_site_name, @@ -54,19 +62,25 @@ RSpec.describe BackupRestore::UploadsRestorer do if remaps.blank? DbHelper.expects(:remap).never else - DbHelper.expects(:remap).with do |from, to, args| - args[:excluded_tables]&.include?("backup_metadata") - remaps.shift == { from: from, to: to } - end.times(remaps.size) + DbHelper + .expects(:remap) + .with do |from, to, args| + args[:excluded_tables]&.include?("backup_metadata") + remaps.shift == { from: from, to: to } + end + .times(remaps.size) end if regex_remaps.blank? DbHelper.expects(:regexp_replace).never else - DbHelper.expects(:regexp_replace).with do |from, to, args| - args[:excluded_tables]&.include?("backup_metadata") - regex_remaps.shift == { from: from, to: to } - end.times(regex_remaps.size) + DbHelper + .expects(:regexp_replace) + .with do |from, to, args| + args[:excluded_tables]&.include?("backup_metadata") + regex_remaps.shift == { from: from, to: to } + end + .times(regex_remaps.size) end if target_site_name == "default" @@ -85,13 +99,14 @@ RSpec.describe BackupRestore::UploadsRestorer do def uploads_path(database) path = File.join("uploads", database) - path = File.join(path, "test_#{ENV['TEST_ENV_NUMBER'].presence || '0'}") + path = File.join(path, "test_#{ENV["TEST_ENV_NUMBER"].presence || "0"}") "/#{path}/" end def s3_url_regex(bucket, path) - Regexp.escape("//#{bucket}") + %q*\.s3(?:\.dualstack\.[a-z0-9\-]+?|[.\-][a-z0-9\-]+?)?\.amazonaws\.com* + Regexp.escape(path) + Regexp.escape("//#{bucket}") + + %q*\.s3(?:\.dualstack\.[a-z0-9\-]+?|[.\-][a-z0-9\-]+?)?\.amazonaws\.com* + Regexp.escape(path) end describe "uploads" do @@ -99,8 +114,8 @@ RSpec.describe BackupRestore::UploadsRestorer do let!(:no_multisite) { { name: "multisite", value: false } } let!(:source_db_name) { { name: "db_name", value: "foo" } } let!(:base_url) { { name: "base_url", value: "https://test.localhost/forum" } } - let!(:no_cdn_url) { { name: "cdn_url", value: nil } } - let!(:cdn_url) { { name: "cdn_url", value: "https://some-cdn.example.com" } } + let!(:no_cdn_url) { { name: "cdn_url", value: nil } } + let!(:cdn_url) { { name: "cdn_url", value: "https://some-cdn.example.com" } } let(:target_site_name) { target_site_type == multisite ? "second" : "default" } let(:target_hostname) { target_site_type == multisite ? "test2.localhost" : "test.localhost" } @@ -127,7 +142,7 @@ RSpec.describe BackupRestore::UploadsRestorer do source_site_name: "foo", target_site_name: "default", from: "/uploads/foo/", - to: uploads_path("default") + to: uploads_path("default"), ) end end @@ -142,13 +157,16 @@ RSpec.describe BackupRestore::UploadsRestorer do post.link_post_uploads FileHelper.stubs(:download).returns(file_from_fixtures("logo.png")) - FileStore::S3Store.any_instance.stubs(:store_upload).returns do - File.join( - "//s3-upload-bucket.s3.dualstack.us-east-1.amazonaws.com", - target_site_type == multisite ? "/uploads/#{target_site_name}" : "", - "original/1X/bc975735dfc6409c1c2aa5ebf2239949bcbdbd65.png" - ) - end + FileStore::S3Store + .any_instance + .stubs(:store_upload) + .returns do + File.join( + "//s3-upload-bucket.s3.dualstack.us-east-1.amazonaws.com", + target_site_type == multisite ? "/uploads/#{target_site_name}" : "", + "original/1X/bc975735dfc6409c1c2aa5ebf2239949bcbdbd65.png", + ) + end UserAvatar.import_url_for_user("logo.png", Fabricate(:user)) end @@ -158,10 +176,11 @@ RSpec.describe BackupRestore::UploadsRestorer do with_temp_uploads_directory do |directory, path| store_class.any_instance.expects(:copy_from).with(path).once - expect { subject.restore(directory) } - .to change { OptimizedImage.count }.by_at_most(-1) - .and change { Jobs::CreateAvatarThumbnails.jobs.size }.by(1) - .and change { Post.where(baked_version: nil).count }.by(1) + expect { subject.restore(directory) }.to change { OptimizedImage.count }.by_at_most( + -1, + ).and change { Jobs::CreateAvatarThumbnails.jobs.size }.by(1).and change { + Post.where(baked_version: nil).count + }.by(1) end end @@ -171,10 +190,11 @@ RSpec.describe BackupRestore::UploadsRestorer do with_temp_uploads_directory(with_optimized: true) do |directory, path| store_class.any_instance.expects(:copy_from).with(path).once - expect { subject.restore(directory) } - .to not_change { OptimizedImage.count } - .and not_change { Jobs::CreateAvatarThumbnails.jobs.size } - .and change { Post.where(baked_version: nil).count }.by(1) + expect { subject.restore(directory) }.to not_change { + OptimizedImage.count + }.and not_change { Jobs::CreateAvatarThumbnails.jobs.size }.and change { + Post.where(baked_version: nil).count + }.by(1) end end end @@ -187,14 +207,14 @@ RSpec.describe BackupRestore::UploadsRestorer do target_site_name: target_site_name, metadata: [source_site_type, base_url], from: "https://test.localhost/forum", - to: "http://localhost" + to: "http://localhost", ) end it "doesn't remap when `cdn_url` in `backup_metadata` is empty" do expect_no_remap( target_site_name: target_site_name, - metadata: [source_site_type, no_cdn_url] + metadata: [source_site_type, no_cdn_url], ) end @@ -206,8 +226,8 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [source_site_type, cdn_url], remaps: [ { from: "https://some-cdn.example.com/", to: "https://new-cdn.example.com/" }, - { from: "some-cdn.example.com", to: "new-cdn.example.com" } - ] + { from: "some-cdn.example.com", to: "new-cdn.example.com" }, + ], ) end @@ -220,8 +240,8 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [source_site_type, cdn_url], remaps: [ { from: "https://some-cdn.example.com/", to: "//example.com/discourse/" }, - { from: "some-cdn.example.com", to: "example.com" } - ] + { from: "some-cdn.example.com", to: "example.com" }, + ], ) end end @@ -230,22 +250,20 @@ RSpec.describe BackupRestore::UploadsRestorer do it "doesn't remap when `s3_base_url` in `backup_metadata` is empty" do expect_no_remap( target_site_name: target_site_name, - metadata: [source_site_type, s3_base_url] + metadata: [source_site_type, s3_base_url], ) end it "doesn't remap when `s3_cdn_url` in `backup_metadata` is empty" do expect_no_remap( target_site_name: target_site_name, - metadata: [source_site_type, s3_cdn_url] + metadata: [source_site_type, s3_cdn_url], ) end end context "when currently stored locally" do - before do - SiteSetting.enable_s3_uploads = false - end + before { SiteSetting.enable_s3_uploads = false } let!(:store_class) { FileStore::LocalStore } @@ -297,7 +315,9 @@ RSpec.describe BackupRestore::UploadsRestorer do end context "with uploads previously stored on S3" do - let!(:s3_base_url) { { name: "s3_base_url", value: "//old-bucket.s3-us-east-1.amazonaws.com" } } + let!(:s3_base_url) do + { name: "s3_base_url", value: "//old-bucket.s3-us-east-1.amazonaws.com" } + end let!(:s3_cdn_url) { { name: "s3_cdn_url", value: "https://s3-cdn.example.com" } } shared_examples "regular site remaps from S3" do @@ -307,7 +327,7 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [no_multisite, s3_base_url], from: s3_url_regex("old-bucket", "/"), to: uploads_path(target_site_name), - regex: true + regex: true, ) end @@ -316,9 +336,12 @@ RSpec.describe BackupRestore::UploadsRestorer do target_site_name: target_site_name, metadata: [no_multisite, s3_cdn_url], remaps: [ - { from: "https://s3-cdn.example.com/", to: "//#{target_hostname}#{uploads_path(target_site_name)}" }, - { from: "s3-cdn.example.com", to: target_hostname } - ] + { + from: "https://s3-cdn.example.com/", + to: "//#{target_hostname}#{uploads_path(target_site_name)}", + }, + { from: "s3-cdn.example.com", to: target_hostname }, + ], ) end end @@ -330,7 +353,7 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [source_db_name, multisite, s3_base_url], from: s3_url_regex("old-bucket", "/"), to: "/", - regex: true + regex: true, ) end @@ -340,8 +363,8 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [source_db_name, multisite, s3_cdn_url], remaps: [ { from: "https://s3-cdn.example.com/", to: "//#{target_hostname}/" }, - { from: "s3-cdn.example.com", to: target_hostname } - ] + { from: "s3-cdn.example.com", to: target_hostname }, + ], ) end end @@ -386,9 +409,7 @@ RSpec.describe BackupRestore::UploadsRestorer do end context "when currently stored on S3" do - before do - setup_s3 - end + before { setup_s3 } let!(:store_class) { FileStore::S3Store } @@ -440,7 +461,9 @@ RSpec.describe BackupRestore::UploadsRestorer do end context "with uploads previously stored on S3" do - let!(:s3_base_url) { { name: "s3_base_url", value: "//old-bucket.s3-us-east-1.amazonaws.com" } } + let!(:s3_base_url) do + { name: "s3_base_url", value: "//old-bucket.s3-us-east-1.amazonaws.com" } + end let!(:s3_cdn_url) { { name: "s3_cdn_url", value: "https://s3-cdn.example.com" } } shared_examples "regular site remaps from S3" do @@ -450,20 +473,26 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [no_multisite, s3_base_url], from: s3_url_regex("old-bucket", "/"), to: uploads_path(target_site_name), - regex: true + regex: true, ) end it "remaps when `s3_cdn_url` changes" do - SiteSetting::Upload.expects(:s3_cdn_url).returns("https://new-s3-cdn.example.com").at_least_once + SiteSetting::Upload + .expects(:s3_cdn_url) + .returns("https://new-s3-cdn.example.com") + .at_least_once expect_remaps( target_site_name: target_site_name, metadata: [no_multisite, s3_cdn_url], remaps: [ - { from: "https://s3-cdn.example.com/", to: "https://new-s3-cdn.example.com#{uploads_path(target_site_name)}" }, - { from: "s3-cdn.example.com", to: "new-s3-cdn.example.com" } - ] + { + from: "https://s3-cdn.example.com/", + to: "https://new-s3-cdn.example.com#{uploads_path(target_site_name)}", + }, + { from: "s3-cdn.example.com", to: "new-s3-cdn.example.com" }, + ], ) end end @@ -475,21 +504,24 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [source_db_name, multisite, s3_base_url], from: s3_url_regex("old-bucket", "/"), to: "/", - regex: true + regex: true, ) end context "when `s3_cdn_url` is configured" do it "remaps when `s3_cdn_url` changes" do - SiteSetting::Upload.expects(:s3_cdn_url).returns("http://new-s3-cdn.example.com").at_least_once + SiteSetting::Upload + .expects(:s3_cdn_url) + .returns("http://new-s3-cdn.example.com") + .at_least_once expect_remaps( target_site_name: target_site_name, metadata: [source_db_name, multisite, s3_cdn_url], remaps: [ { from: "https://s3-cdn.example.com/", to: "//new-s3-cdn.example.com/" }, - { from: "s3-cdn.example.com", to: "new-s3-cdn.example.com" } - ] + { from: "s3-cdn.example.com", to: "new-s3-cdn.example.com" }, + ], ) end end @@ -503,8 +535,8 @@ RSpec.describe BackupRestore::UploadsRestorer do metadata: [source_db_name, multisite, s3_cdn_url], remaps: [ { from: "https://s3-cdn.example.com/", to: "//#{target_hostname}/" }, - { from: "s3-cdn.example.com", to: target_hostname } - ] + { from: "s3-cdn.example.com", to: target_hostname }, + ], ) end end @@ -593,7 +625,7 @@ RSpec.describe BackupRestore::UploadsRestorer do source_site_name: "xylan", target_site_name: "default", from: "/uploads/xylan/", - to: uploads_path("default") + to: uploads_path("default"), ) do |directory| FileUtils.mkdir_p(File.join(directory, "uploads", "PaxHeaders.27134")) FileUtils.mkdir_p(File.join(directory, "uploads", ".hidden")) diff --git a/spec/lib/bookmark_manager_spec.rb b/spec/lib/bookmark_manager_spec.rb index e362a061997..164cee18727 100644 --- a/spec/lib/bookmark_manager_spec.rb +++ b/spec/lib/bookmark_manager_spec.rb @@ -5,7 +5,7 @@ RSpec.describe BookmarkManager do let(:reminder_at) { 1.day.from_now } fab!(:post) { Fabricate(:post) } - let(:name) { 'Check this out!' } + let(:name) { "Check this out!" } subject { described_class.new(user) } @@ -40,7 +40,14 @@ RSpec.describe BookmarkManager do end describe ".update" do - let!(:bookmark) { Fabricate(:bookmark_next_business_day_reminder, user: user, bookmarkable: post, name: "Old name") } + let!(:bookmark) do + Fabricate( + :bookmark_next_business_day_reminder, + user: user, + bookmarkable: post, + name: "Old name", + ) + end let(:new_name) { "Some new name" } let(:new_reminder_at) { 10.days.from_now } let(:options) { {} } @@ -50,7 +57,7 @@ RSpec.describe BookmarkManager do bookmark_id: bookmark.id, name: new_name, reminder_at: new_reminder_at, - options: options + options: options, ) end @@ -69,7 +76,9 @@ RSpec.describe BookmarkManager do end context "when options are provided" do - let(:options) { { auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent] } } + let(:options) do + { auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent] } + end it "saves any additional options successfully" do update_bookmark @@ -86,9 +95,7 @@ RSpec.describe BookmarkManager do end context "if the bookmark no longer exists" do - before do - bookmark.destroy! - end + before { bookmark.destroy! } it "raises a not found error" do expect { update_bookmark }.to raise_error(Discourse::NotFound) end @@ -97,8 +104,12 @@ RSpec.describe BookmarkManager do describe ".destroy_for_topic" do let!(:topic) { Fabricate(:topic) } - let!(:bookmark1) { Fabricate(:bookmark, bookmarkable: Fabricate(:post, topic: topic), user: user) } - let!(:bookmark2) { Fabricate(:bookmark, bookmarkable: Fabricate(:post, topic: topic), user: user) } + let!(:bookmark1) do + Fabricate(:bookmark, bookmarkable: Fabricate(:post, topic: topic), user: user) + end + let!(:bookmark2) do + Fabricate(:bookmark, bookmarkable: Fabricate(:post, topic: topic), user: user) + end it "destroys all bookmarks for the topic for the specified user" do subject.destroy_for_topic(topic) @@ -136,9 +147,7 @@ RSpec.describe BookmarkManager do end context "when the bookmark does no longer exist" do - before do - bookmark.destroy - end + before { bookmark.destroy } it "does not error, and does not create a notification" do described_class.send_reminder_notification(bookmark.id) expect(notifications_for_user.any?).to eq(false) @@ -146,9 +155,7 @@ RSpec.describe BookmarkManager do end context "if the post has been deleted" do - before do - bookmark.bookmarkable.trash! - end + before { bookmark.bookmarkable.trash! } it "does not error and does not create a notification" do described_class.send_reminder_notification(bookmark.id) bookmark.reload @@ -157,7 +164,10 @@ RSpec.describe BookmarkManager do end def notifications_for_user - Notification.where(notification_type: Notification.types[:bookmark_reminder], user_id: bookmark.user.id) + Notification.where( + notification_type: Notification.types[:bookmark_reminder], + user_id: bookmark.user.id, + ) end end @@ -179,14 +189,14 @@ RSpec.describe BookmarkManager do context "if the bookmark is belonging to some other user" do let!(:bookmark) { Fabricate(:bookmark, user: Fabricate(:admin)) } it "raises an invalid access error" do - expect { subject.toggle_pin(bookmark_id: bookmark.id) }.to raise_error(Discourse::InvalidAccess) + expect { subject.toggle_pin(bookmark_id: bookmark.id) }.to raise_error( + Discourse::InvalidAccess, + ) end end context "if the bookmark no longer exists" do - before do - bookmark.destroy! - end + before { bookmark.destroy! } it "raises a not found error" do expect { subject.toggle_pin(bookmark_id: bookmark.id) }.to raise_error(Discourse::NotFound) end @@ -220,11 +230,18 @@ RSpec.describe BookmarkManager do it "adds a validation error when the bookmarkable_type is not registered" do subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "BlahFactory", name: name) - expect(subject.errors.full_messages).to include(I18n.t("bookmarks.errors.invalid_bookmarkable", type: "BlahFactory")) + expect(subject.errors.full_messages).to include( + I18n.t("bookmarks.errors.invalid_bookmarkable", type: "BlahFactory"), + ) end it "updates the topic user bookmarked column to true if any post is bookmarked" do - subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at) + subject.create_for( + bookmarkable_id: post.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + ) tu = TopicUser.find_by(user: user) expect(tu.bookmarked).to eq(true) tu.update(bookmarked: false) @@ -235,35 +252,63 @@ RSpec.describe BookmarkManager do end it "sets auto_delete_preference to clear_reminder by default" do - bookmark = subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at) - expect(bookmark.auto_delete_preference).to eq(Bookmark.auto_delete_preferences[:clear_reminder]) - end - - context "when the user has set their bookmark_auto_delete_preference" do - before do - user.user_option.update!(bookmark_auto_delete_preference: Bookmark.auto_delete_preferences[:on_owner_reply]) - end - - it "sets auto_delete_preferences to the user's user_option.bookmark_auto_delete_preference" do - bookmark = subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at) - expect(bookmark.auto_delete_preference).to eq(Bookmark.auto_delete_preferences[:on_owner_reply]) - end - - it "uses the passed in auto_delete_preference option instead of the user's one" do - bookmark = subject.create_for( + bookmark = + subject.create_for( bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at, - options: { auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent] } ) - expect(bookmark.auto_delete_preference).to eq(Bookmark.auto_delete_preferences[:when_reminder_sent]) + expect(bookmark.auto_delete_preference).to eq( + Bookmark.auto_delete_preferences[:clear_reminder], + ) + end + + context "when the user has set their bookmark_auto_delete_preference" do + before do + user.user_option.update!( + bookmark_auto_delete_preference: Bookmark.auto_delete_preferences[:on_owner_reply], + ) + end + + it "sets auto_delete_preferences to the user's user_option.bookmark_auto_delete_preference" do + bookmark = + subject.create_for( + bookmarkable_id: post.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + ) + expect(bookmark.auto_delete_preference).to eq( + Bookmark.auto_delete_preferences[:on_owner_reply], + ) + end + + it "uses the passed in auto_delete_preference option instead of the user's one" do + bookmark = + subject.create_for( + bookmarkable_id: post.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + options: { + auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent], + }, + ) + expect(bookmark.auto_delete_preference).to eq( + Bookmark.auto_delete_preferences[:when_reminder_sent], + ) end end context "when a reminder time is provided" do it "saves the values correctly" do - subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at) + subject.create_for( + bookmarkable_id: post.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + ) bookmark = Bookmark.find_by(user: user, bookmarkable: post) expect(bookmark.reminder_at).to eq_time(reminder_at) @@ -272,10 +317,18 @@ RSpec.describe BookmarkManager do end context "when options are provided" do - let(:options) { { auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent] } } + let(:options) do + { auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent] } + end it "saves any additional options successfully" do - subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at, options: options) + subject.create_for( + bookmarkable_id: post.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + options: options, + ) bookmark = Bookmark.find_by(user: user, bookmarkable: post) expect(bookmark.auto_delete_preference).to eq(1) @@ -283,20 +336,22 @@ RSpec.describe BookmarkManager do end context "when the bookmark already exists for the user & post" do - before do - Bookmark.create(bookmarkable: post, user: user) - end + before { Bookmark.create(bookmarkable: post, user: user) } it "adds an error to the manager" do subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post") - expect(subject.errors.full_messages).to include(I18n.t("bookmarks.errors.already_bookmarked", type: "Post")) + expect(subject.errors.full_messages).to include( + I18n.t("bookmarks.errors.already_bookmarked", type: "Post"), + ) end end context "when the bookmark name is too long" do it "adds an error to the manager" do subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: "test" * 100) - expect(subject.errors.full_messages).to include("Name is too long (maximum is 100 characters)") + expect(subject.errors.full_messages).to include( + "Name is too long (maximum is 100 characters)", + ) end end @@ -304,8 +359,15 @@ RSpec.describe BookmarkManager do let(:reminder_at) { 10.days.ago } it "adds an error to the manager" do - subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at) - expect(subject.errors.full_messages).to include(I18n.t("bookmarks.errors.cannot_set_past_reminder")) + subject.create_for( + bookmarkable_id: post.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + ) + expect(subject.errors.full_messages).to include( + I18n.t("bookmarks.errors.cannot_set_past_reminder"), + ) end end @@ -313,26 +375,33 @@ RSpec.describe BookmarkManager do let(:reminder_at) { 11.years.from_now } it "adds an error to the manager" do - subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name, reminder_at: reminder_at) - expect(subject.errors.full_messages).to include(I18n.t("bookmarks.errors.cannot_set_reminder_in_distant_future")) + subject.create_for( + bookmarkable_id: post.id, + bookmarkable_type: "Post", + name: name, + reminder_at: reminder_at, + ) + expect(subject.errors.full_messages).to include( + I18n.t("bookmarks.errors.cannot_set_reminder_in_distant_future"), + ) end end context "when the post is inaccessible for the user" do - before do - post.trash! - end + before { post.trash! } it "raises an invalid access error" do - expect { subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name) }.to raise_error(Discourse::InvalidAccess) + expect { + subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name) + }.to raise_error(Discourse::InvalidAccess) end end context "when the topic is inaccessible for the user" do - before do - post.topic.update(category: Fabricate(:private_category, group: Fabricate(:group))) - end + before { post.topic.update(category: Fabricate(:private_category, group: Fabricate(:group))) } it "raises an invalid access error" do - expect { subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name) }.to raise_error(Discourse::InvalidAccess) + expect { + subject.create_for(bookmarkable_id: post.id, bookmarkable_type: "Post", name: name) + }.to raise_error(Discourse::InvalidAccess) end end end diff --git a/spec/lib/bookmark_query_spec.rb b/spec/lib/bookmark_query_spec.rb index 69d218aa572..0017dfbf595 100644 --- a/spec/lib/bookmark_query_spec.rb +++ b/spec/lib/bookmark_query_spec.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true RSpec.describe BookmarkQuery do - before do - SearchIndexer.enable - end + before { SearchIndexer.enable } fab!(:user) { Fabricate(:user) } let(:params) { {} } @@ -24,12 +22,12 @@ RSpec.describe BookmarkQuery do let(:post_bookmark) { Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:post)) } let(:topic_bookmark) { Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:topic)) } - let(:user_bookmark) { Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:user, username: "bookmarkqueen")) } - - after do - Bookmark.reset_bookmarkables + let(:user_bookmark) do + Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:user, username: "bookmarkqueen")) end + after { Bookmark.reset_bookmarkables } + it "returns all the bookmarks for a user" do expect(bookmark_query.list_all.count).to eq(3) end @@ -42,16 +40,16 @@ RSpec.describe BookmarkQuery do it "runs the on_preload block provided passing in bookmarks" do preloaded_bookmarks = [] - BookmarkQuery.on_preload do |bookmarks, bq| - (preloaded_bookmarks << bookmarks).flatten - end + BookmarkQuery.on_preload { |bookmarks, bq| (preloaded_bookmarks << bookmarks).flatten } bookmark_query.list_all expect(preloaded_bookmarks.any?).to eq(true) end it "returns a mixture of post, topic, and custom bookmarkable type bookmarks" do bookmarks = bookmark_query.list_all - expect(bookmarks.map(&:id)).to match_array([post_bookmark.id, topic_bookmark.id, user_bookmark.id]) + expect(bookmarks.map(&:id)).to match_array( + [post_bookmark.id, topic_bookmark.id, user_bookmark.id], + ) end it "handles the user not having permission for all of the bookmarks of a certain bookmarkable" do @@ -61,7 +59,9 @@ RSpec.describe BookmarkQuery do end it "handles the user not having permission to see any of their bookmarks" do - topic_bookmark.bookmarkable.update(category: Fabricate(:private_category, group: Fabricate(:group))) + topic_bookmark.bookmarkable.update( + category: Fabricate(:private_category, group: Fabricate(:group)), + ) post_bookmark.bookmarkable.topic.update(category: topic_bookmark.bookmarkable.category) UserTestBookmarkable.expects(:list_query).returns(nil) bookmarks = bookmark_query.list_all @@ -69,17 +69,21 @@ RSpec.describe BookmarkQuery do end context "when q param is provided" do - let!(:post) { Fabricate(:post, raw: "Some post content here", topic: Fabricate(:topic, title: "Bugfix game for devs")) } - - before do - Bookmark.reset_bookmarkables + let!(:post) do + Fabricate( + :post, + raw: "Some post content here", + topic: Fabricate(:topic, title: "Bugfix game for devs"), + ) end - after do - Bookmark.reset_bookmarkables - end + before { Bookmark.reset_bookmarkables } - let(:bookmark3) { Fabricate(:bookmark, user: user, name: "Check up later", bookmarkable: Fabricate(:post)) } + after { Bookmark.reset_bookmarkables } + + let(:bookmark3) do + Fabricate(:bookmark, user: user, name: "Check up later", bookmarkable: Fabricate(:post)) + end let(:bookmark4) { Fabricate(:bookmark, user: user, bookmarkable: post) } before do @@ -88,29 +92,29 @@ RSpec.describe BookmarkQuery do end it "can search by bookmark name" do - bookmarks = bookmark_query(params: { q: 'check' }).list_all + bookmarks = bookmark_query(params: { q: "check" }).list_all expect(bookmarks.map(&:id)).to eq([bookmark3.id]) end it "can search by post content" do - bookmarks = bookmark_query(params: { q: 'content' }).list_all + bookmarks = bookmark_query(params: { q: "content" }).list_all expect(bookmarks.map(&:id)).to eq([bookmark4.id]) end it "can search by topic title" do - bookmarks = bookmark_query(params: { q: 'bugfix' }).list_all + bookmarks = bookmark_query(params: { q: "bugfix" }).list_all expect(bookmarks.map(&:id)).to eq([bookmark4.id]) end context "with custom bookmarkable fitering" do - before do - register_test_bookmarkable + before { register_test_bookmarkable } + + let!(:bookmark5) do + Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:user, username: "bookmarkking")) end - let!(:bookmark5) { Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:user, username: "bookmarkking")) } - it "allows searching bookmarkables by fields in other tables" do - bookmarks = bookmark_query(params: { q: 'bookmarkk' }).list_all + bookmarks = bookmark_query(params: { q: "bookmarkk" }).list_all expect(bookmarks.map(&:id)).to eq([bookmark5.id]) end end @@ -157,9 +161,7 @@ RSpec.describe BookmarkQuery do end context "when the user is a topic_allowed_user" do - before do - TopicAllowedUser.create(topic: pm_topic, user: user) - end + before { TopicAllowedUser.create(topic: pm_topic, user: user) } it "shows the user the bookmark in the PM" do expect(bookmark_query.list_all.map(&:id).count).to eq(3) end @@ -192,7 +194,9 @@ RSpec.describe BookmarkQuery do context "when the topic category is private" do let(:group) { Fabricate(:group) } before do - post_bookmark.bookmarkable.topic.update(category: Fabricate(:private_category, group: group)) + post_bookmark.bookmarkable.topic.update( + category: Fabricate(:private_category, group: group), + ) post_bookmark.reload end it "does not show the user a post/topic in a private category they cannot see" do @@ -227,26 +231,18 @@ RSpec.describe BookmarkQuery do end it "order defaults to updated_at DESC" do - expect(bookmark_query.list_all.map(&:id)).to eq([ - bookmark1.id, - bookmark2.id, - bookmark5.id, - bookmark4.id, - bookmark3.id - ]) + expect(bookmark_query.list_all.map(&:id)).to eq( + [bookmark1.id, bookmark2.id, bookmark5.id, bookmark4.id, bookmark3.id], + ) end it "orders by reminder_at, then updated_at" do bookmark4.update_column(:reminder_at, 1.day.from_now) bookmark5.update_column(:reminder_at, 26.hours.from_now) - expect(bookmark_query.list_all.map(&:id)).to eq([ - bookmark4.id, - bookmark5.id, - bookmark1.id, - bookmark2.id, - bookmark3.id - ]) + expect(bookmark_query.list_all.map(&:id)).to eq( + [bookmark4.id, bookmark5.id, bookmark1.id, bookmark2.id, bookmark3.id], + ) end it "shows pinned bookmarks first ordered by reminder_at ASC then updated_at DESC" do @@ -261,13 +257,9 @@ RSpec.describe BookmarkQuery do bookmark5.update_column(:reminder_at, 1.day.from_now) - expect(bookmark_query.list_all.map(&:id)).to eq([ - bookmark3.id, - bookmark4.id, - bookmark1.id, - bookmark2.id, - bookmark5.id - ]) + expect(bookmark_query.list_all.map(&:id)).to eq( + [bookmark3.id, bookmark4.id, bookmark1.id, bookmark2.id, bookmark5.id], + ) end end end diff --git a/spec/lib/bookmark_reminder_notification_handler_spec.rb b/spec/lib/bookmark_reminder_notification_handler_spec.rb index cf660394cee..9a77ad9bb15 100644 --- a/spec/lib/bookmark_reminder_notification_handler_spec.rb +++ b/spec/lib/bookmark_reminder_notification_handler_spec.rb @@ -5,9 +5,7 @@ RSpec.describe BookmarkReminderNotificationHandler do fab!(:user) { Fabricate(:user) } - before do - Discourse.redis.flushdb - end + before { Discourse.redis.flushdb } describe "#send_notification" do let!(:bookmark) do @@ -42,7 +40,9 @@ RSpec.describe BookmarkReminderNotificationHandler do context "when the auto_delete_preference is when_reminder_sent" do before do TopicUser.create!(topic: bookmark.bookmarkable.topic, user: user, bookmarked: true) - bookmark.update(auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent]) + bookmark.update( + auto_delete_preference: Bookmark.auto_delete_preferences[:when_reminder_sent], + ) end it "deletes the bookmark after the reminder gets sent" do @@ -52,17 +52,25 @@ RSpec.describe BookmarkReminderNotificationHandler do it "changes the TopicUser bookmarked column to false" do subject.new(bookmark).send_notification - expect(TopicUser.find_by(topic: bookmark.bookmarkable.topic, user: user).bookmarked).to eq(false) + expect(TopicUser.find_by(topic: bookmark.bookmarkable.topic, user: user).bookmarked).to eq( + false, + ) end context "if there are still other bookmarks in the topic" do before do - Fabricate(:bookmark, bookmarkable: Fabricate(:post, topic: bookmark.bookmarkable.topic), user: user) + Fabricate( + :bookmark, + bookmarkable: Fabricate(:post, topic: bookmark.bookmarkable.topic), + user: user, + ) end it "does not change the TopicUser bookmarked column to false" do subject.new(bookmark).send_notification - expect(TopicUser.find_by(topic: bookmark.bookmarkable.topic, user: user).bookmarked).to eq(true) + expect( + TopicUser.find_by(topic: bookmark.bookmarkable.topic, user: user).bookmarked, + ).to eq(true) end end end diff --git a/spec/lib/browser_detection_spec.rb b/spec/lib/browser_detection_spec.rb index d0820bc511c..0232020c7c9 100644 --- a/spec/lib/browser_detection_spec.rb +++ b/spec/lib/browser_detection_spec.rb @@ -1,42 +1,150 @@ # frozen_string_literal: true -require 'browser_detection' +require "browser_detection" RSpec.describe BrowserDetection do - it "detects browser, device and operating system" do [ ["Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)", :ie, :windows, :windows], - ["Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)", :ie, :windows, :windows], - ["Mozilla/5.0 (iPad; CPU OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13F69 Safari/601.1", :safari, :ipad, :ios], - ["Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1", :safari, :iphone, :ios], - ["Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/51.0.2704.104 Mobile/13F69 Safari/601.1.46", :chrome, :iphone, :ios], - ["Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19", :chrome, :android, :android], - ["Mozilla/5.0 (Linux; Android 4.4.2; XMP-6250 Build/HAWK) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 ADAPI/2.0 (UUID:9e7df0ed-2a5c-4a19-bec7-2cc54800f99d) RK3188-ADAPI/1.2.84.533 (MODEL:XMP-6250)", :chrome, :android, :android], - ["Mozilla/5.0 (Linux; Android 5.1; Nexus 7 Build/LMY47O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.105 Safari/537.36", :chrome, :android, :android], - ["Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", :chrome, :android, :android], - ["Mozilla/5.0 (Linux; Android; 4.1.2; GT-I9100 Build/000000) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1234.12 Mobile Safari/537.22 OPR/14.0.123.123", :opera, :android, :android], - ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0", :firefox, :mac, :macos], - ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0", :firefox, :mac, :macos], - ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36", :chrome, :mac, :macos], - ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36", :chrome, :windows, :windows], - ["Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1", :firefox, :windows, :windows], + [ + "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)", + :ie, + :windows, + :windows, + ], + [ + "Mozilla/5.0 (iPad; CPU OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13F69 Safari/601.1", + :safari, + :ipad, + :ios, + ], + [ + "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1", + :safari, + :iphone, + :ios, + ], + [ + "Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/51.0.2704.104 Mobile/13F69 Safari/601.1.46", + :chrome, + :iphone, + :ios, + ], + [ + "Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19", + :chrome, + :android, + :android, + ], + [ + "Mozilla/5.0 (Linux; Android 4.4.2; XMP-6250 Build/HAWK) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 ADAPI/2.0 (UUID:9e7df0ed-2a5c-4a19-bec7-2cc54800f99d) RK3188-ADAPI/1.2.84.533 (MODEL:XMP-6250)", + :chrome, + :android, + :android, + ], + [ + "Mozilla/5.0 (Linux; Android 5.1; Nexus 7 Build/LMY47O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.105 Safari/537.36", + :chrome, + :android, + :android, + ], + [ + "Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", + :chrome, + :android, + :android, + ], + [ + "Mozilla/5.0 (Linux; Android; 4.1.2; GT-I9100 Build/000000) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1234.12 Mobile Safari/537.22 OPR/14.0.123.123", + :opera, + :android, + :android, + ], + [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0", + :firefox, + :mac, + :macos, + ], + [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0", + :firefox, + :mac, + :macos, + ], + [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36", + :chrome, + :mac, + :macos, + ], + [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36", + :chrome, + :windows, + :windows, + ], + [ + "Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1", + :firefox, + :windows, + :windows, + ], ["Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko", :ie, :windows, :windows], - ["Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36", :chrome, :windows, :windows], - ["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1", :firefox, :windows, :windows], - ["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0", :firefox, :windows, :windows], - ["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36", :chrome, :linux, :linux], - ["Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/47.0 (Chrome)", :firefox, :linux, :linux], + [ + "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36", + :chrome, + :windows, + :windows, + ], + [ + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1", + :firefox, + :windows, + :windows, + ], + [ + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0", + :firefox, + :windows, + :windows, + ], + [ + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36", + :chrome, + :linux, + :linux, + ], + [ + "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/47.0 (Chrome)", + :firefox, + :linux, + :linux, + ], ["Opera/9.80 (X11; Linux zvav; U; en) Presto/2.12.423 Version/12.16", :opera, :linux, :linux], - ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246", :edge, :windows, :windows], - ["Mozilla/5.0 (X11; CrOS x86_64 11895.95.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.125 Safari/537.36 ", :chrome, :chromebook, :chromeos], - ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edg/75.10240", :edge, :windows, :windows], - ["Discourse/163 CFNetwork/978.0.7 Darwin/18.6.0", :discoursehub, :unknown, :ios] + [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246", + :edge, + :windows, + :windows, + ], + [ + "Mozilla/5.0 (X11; CrOS x86_64 11895.95.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.125 Safari/537.36 ", + :chrome, + :chromebook, + :chromeos, + ], + [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edg/75.10240", + :edge, + :windows, + :windows, + ], + ["Discourse/163 CFNetwork/978.0.7 Darwin/18.6.0", :discoursehub, :unknown, :ios], ].each do |user_agent, browser, device, os| expect(BrowserDetection.browser(user_agent)).to eq(browser) expect(BrowserDetection.device(user_agent)).to eq(device) expect(BrowserDetection.os(user_agent)).to eq(os) end end - end diff --git a/spec/lib/cache_spec.rb b/spec/lib/cache_spec.rb index 19d79d81bae..ae09bcc39b6 100644 --- a/spec/lib/cache_spec.rb +++ b/spec/lib/cache_spec.rb @@ -1,9 +1,8 @@ # frozen_string_literal: true -require 'cache' +require "cache" RSpec.describe Cache do - let :cache do Cache.new end @@ -43,9 +42,7 @@ RSpec.describe Cache do it "can delete correctly" do cache.delete("key") - cache.fetch("key", expires_in: 1.minute) do - "test" - end + cache.fetch("key", expires_in: 1.minute) { "test" } expect(cache.fetch("key")).to eq("test") @@ -59,9 +56,7 @@ RSpec.describe Cache do key = cache.normalize_key("key") - cache.fetch("key", expires_in: 1.minute) do - "bob" - end + cache.fetch("key", expires_in: 1.minute) { "bob" } expect(Discourse.redis.ttl(key)).to be_within(2.seconds).of(1.minute) @@ -75,9 +70,10 @@ RSpec.describe Cache do it "can store and fetch correctly" do cache.delete "key" - r = cache.fetch "key" do - "bob" - end + r = + cache.fetch "key" do + "bob" + end expect(r).to eq("bob") end @@ -85,9 +81,10 @@ RSpec.describe Cache do it "can fetch existing correctly" do cache.write "key", "bill" - r = cache.fetch "key" do - "bob" - end + r = + cache.fetch "key" do + "bob" + end expect(r).to eq("bill") end diff --git a/spec/lib/category_badge_spec.rb b/spec/lib/category_badge_spec.rb index c6d04cead2b..3d36d1af035 100644 --- a/spec/lib/category_badge_spec.rb +++ b/spec/lib/category_badge_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require 'category_badge' +require "category_badge" RSpec.describe CategoryBadge do it "escapes HTML in category names / descriptions" do - c = Fabricate(:category, name: 'name', description: 'title') + c = Fabricate(:category, name: "name", description: "title") html = CategoryBadge.html_for(c) diff --git a/spec/lib/category_guardian_spec.rb b/spec/lib/category_guardian_spec.rb index a2679c81d39..0cbd94cea1a 100644 --- a/spec/lib/category_guardian_spec.rb +++ b/spec/lib/category_guardian_spec.rb @@ -22,7 +22,13 @@ RSpec.describe CategoryGuardian do context "when restricted category" do fab!(:group) { Fabricate(:group) } - fab!(:category) { Fabricate(:private_category, group: group, permission_type: CategoryGroup.permission_types[:readonly]) } + fab!(:category) do + Fabricate( + :private_category, + group: group, + permission_type: CategoryGroup.permission_types[:readonly], + ) + end fab!(:group_user) { Fabricate(:group_user, group: group, user: user) } it "returns false for anonymous user" do @@ -38,12 +44,22 @@ RSpec.describe CategoryGuardian do end it "returns true for member of group with create_post access" do - category = Fabricate(:private_category, group: group, permission_type: CategoryGroup.permission_types[:create_post]) + category = + Fabricate( + :private_category, + group: group, + permission_type: CategoryGroup.permission_types[:create_post], + ) expect(Guardian.new(user).can_post_in_category?(category)).to eq(true) end it "returns true for member of group with full access" do - category = Fabricate(:private_category, group: group, permission_type: CategoryGroup.permission_types[:full]) + category = + Fabricate( + :private_category, + group: group, + permission_type: CategoryGroup.permission_types[:full], + ) expect(Guardian.new(user).can_post_in_category?(category)).to eq(true) end end diff --git a/spec/lib/common_passwords/common_passwords_spec.rb b/spec/lib/common_passwords/common_passwords_spec.rb index fba5e6804f8..7373882a207 100644 --- a/spec/lib/common_passwords/common_passwords_spec.rb +++ b/spec/lib/common_passwords/common_passwords_spec.rb @@ -12,7 +12,7 @@ RSpec.describe CommonPasswords do it "returns false if password isn't in the common passwords list" do described_class.stubs(:password_list).returns(stub_everything(include?: false)) - @password = 'uncommonPassword' + @password = "uncommonPassword" expect(subject).to eq(false) end @@ -35,19 +35,19 @@ RSpec.describe CommonPasswords do end end - describe '#password_list' do + describe "#password_list" do before { Discourse.redis.flushdb } after { Discourse.redis.flushdb } it "loads the passwords file if redis doesn't have it" do Discourse.redis.without_namespace.stubs(:scard).returns(0) - described_class.expects(:load_passwords).returns(['password']) + described_class.expects(:load_passwords).returns(["password"]) list = described_class.password_list expect(list).to respond_to(:include?) end it "doesn't load the passwords file if redis has it" do - Discourse.redis.without_namespace.stubs(:scard).returns(10000) + Discourse.redis.without_namespace.stubs(:scard).returns(10_000) described_class.expects(:load_passwords).never list = described_class.password_list expect(list).to respond_to(:include?) @@ -55,7 +55,7 @@ RSpec.describe CommonPasswords do it "loads the passwords file if redis has an empty list" do Discourse.redis.without_namespace.stubs(:scard).returns(0) - described_class.expects(:load_passwords).returns(['password']) + described_class.expects(:load_passwords).returns(["password"]) list = described_class.password_list expect(list).to respond_to(:include?) end diff --git a/spec/lib/composer_messages_finder_spec.rb b/spec/lib/composer_messages_finder_spec.rb index 7ff75f30f34..1d7ad381a81 100644 --- a/spec/lib/composer_messages_finder_spec.rb +++ b/spec/lib/composer_messages_finder_spec.rb @@ -1,12 +1,12 @@ # encoding: utf-8 # frozen_string_literal: true -require 'composer_messages_finder' +require "composer_messages_finder" RSpec.describe ComposerMessagesFinder do describe "delegates work" do let(:user) { Fabricate.build(:user) } - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'createTopic') } + let(:finder) { ComposerMessagesFinder.new(user, composer_action: "createTopic") } it "calls all the message finders" do finder.expects(:check_education_message).once @@ -20,15 +20,13 @@ RSpec.describe ComposerMessagesFinder do end end - describe '.check_education_message' do + describe ".check_education_message" do let(:user) { Fabricate.build(:user) } - context 'when creating topic' do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'createTopic') } + context "when creating topic" do + let(:finder) { ComposerMessagesFinder.new(user, composer_action: "createTopic") } - before do - SiteSetting.educate_until_posts = 10 - end + before { SiteSetting.educate_until_posts = 10 } it "returns a message for a user who has not posted any topics" do user.expects(:created_topic_count).returns(9) @@ -41,32 +39,34 @@ RSpec.describe ComposerMessagesFinder do end end - context 'with private message' do + context "with private message" do fab!(:topic) { Fabricate(:private_message_topic) } - context 'when starting a new private message' do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'createTopic', topic_id: topic.id) } + context "when starting a new private message" do + let(:finder) do + ComposerMessagesFinder.new(user, composer_action: "createTopic", topic_id: topic.id) + end - it 'should return an empty string' do + it "should return an empty string" do expect(finder.check_education_message).to eq(nil) end end - context 'when replying to a private message' do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply', topic_id: topic.id) } + context "when replying to a private message" do + let(:finder) do + ComposerMessagesFinder.new(user, composer_action: "reply", topic_id: topic.id) + end - it 'should return an empty string' do + it "should return an empty string" do expect(finder.check_education_message).to eq(nil) end end end - context 'when creating reply' do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply') } + context "when creating reply" do + let(:finder) { ComposerMessagesFinder.new(user, composer_action: "reply") } - before do - SiteSetting.educate_until_posts = 10 - end + before { SiteSetting.educate_until_posts = 10 } it "returns a message for a user who has not posted any topics" do user.expects(:post_count).returns(9) @@ -80,11 +80,11 @@ RSpec.describe ComposerMessagesFinder do end end - describe '.check_new_user_many_replies' do + describe ".check_new_user_many_replies" do let(:user) { Fabricate.build(:user) } - context 'when replying' do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply') } + context "when replying" do + let(:finder) { ComposerMessagesFinder.new(user, composer_action: "reply") } it "has no message when `posted_too_much_in_topic?` is false" do user.expects(:posted_too_much_in_topic?).returns(false) @@ -96,11 +96,10 @@ RSpec.describe ComposerMessagesFinder do expect(finder.check_new_user_many_replies).to be_present end end - end - describe '.check_avatar_notification' do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'createTopic') } + describe ".check_avatar_notification" do + let(:finder) { ComposerMessagesFinder.new(user, composer_action: "createTopic") } fab!(:user) { Fabricate(:user) } context "with success" do @@ -126,7 +125,10 @@ RSpec.describe ComposerMessagesFinder do end it "doesn't notify users who have been notified already" do - UserHistory.create!(action: UserHistory.actions[:notified_about_avatar], target_user_id: user.id) + UserHistory.create!( + action: UserHistory.actions[:notified_about_avatar], + target_user_id: user.id, + ) expect(finder.check_avatar_notification).to be_blank end @@ -141,12 +143,12 @@ RSpec.describe ComposerMessagesFinder do end it "doesn't notify users if 'allow_uploaded_avatars' setting is disabled" do - SiteSetting.allow_uploaded_avatars = 'disabled' + SiteSetting.allow_uploaded_avatars = "disabled" expect(finder.check_avatar_notification).to be_blank end end - describe '.check_sequential_replies' do + describe ".check_sequential_replies" do fab!(:user) { Fabricate(:user) } fab!(:topic) { Fabricate(:topic) } @@ -164,16 +166,20 @@ RSpec.describe ComposerMessagesFinder do end it "does not give a message for new topics" do - finder = ComposerMessagesFinder.new(user, composer_action: 'createTopic') + finder = ComposerMessagesFinder.new(user, composer_action: "createTopic") expect(finder.check_sequential_replies).to be_blank end it "does not give a message without a topic id" do - expect(ComposerMessagesFinder.new(user, composer_action: 'reply').check_sequential_replies).to be_blank + expect( + ComposerMessagesFinder.new(user, composer_action: "reply").check_sequential_replies, + ).to be_blank end context "with reply" do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply', topic_id: topic.id) } + let(:finder) do + ComposerMessagesFinder.new(user, composer_action: "reply", topic_id: topic.id) + end it "does not give a message to users who are still in the 'education' phase" do user.stubs(:post_count).returns(9) @@ -181,12 +187,20 @@ RSpec.describe ComposerMessagesFinder do end it "doesn't notify a user it has already notified about sequential replies" do - UserHistory.create!(action: UserHistory.actions[:notified_about_sequential_replies], target_user_id: user.id, topic_id: topic.id) + UserHistory.create!( + action: UserHistory.actions[:notified_about_sequential_replies], + target_user_id: user.id, + topic_id: topic.id, + ) expect(finder.check_sequential_replies).to be_blank end it "will notify you if it hasn't in the current topic" do - UserHistory.create!(action: UserHistory.actions[:notified_about_sequential_replies], target_user_id: user.id, topic_id: topic.id + 1) + UserHistory.create!( + action: UserHistory.actions[:notified_about_sequential_replies], + target_user_id: user.id, + topic_id: topic.id + 1, + ) expect(finder.check_sequential_replies).to be_present end @@ -215,13 +229,11 @@ RSpec.describe ComposerMessagesFinder do it "creates a notified_about_sequential_replies log" do expect(UserHistory.exists_for_user?(user, :notified_about_sequential_replies)).to eq(true) end - end end - end - describe '.check_dominating_topic' do + describe ".check_dominating_topic" do fab!(:user) { Fabricate(:user) } fab!(:topic) { Fabricate(:topic) } @@ -239,16 +251,20 @@ RSpec.describe ComposerMessagesFinder do end it "does not give a message for new topics" do - finder = ComposerMessagesFinder.new(user, composer_action: 'createTopic') + finder = ComposerMessagesFinder.new(user, composer_action: "createTopic") expect(finder.check_dominating_topic).to be_blank end it "does not give a message without a topic id" do - expect(ComposerMessagesFinder.new(user, composer_action: 'reply').check_dominating_topic).to be_blank + expect( + ComposerMessagesFinder.new(user, composer_action: "reply").check_dominating_topic, + ).to be_blank end context "with reply" do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply', topic_id: topic.id) } + let(:finder) do + ComposerMessagesFinder.new(user, composer_action: "reply", topic_id: topic.id) + end it "does not give a message to users who are still in the 'education' phase" do user.stubs(:post_count).returns(9) @@ -261,12 +277,20 @@ RSpec.describe ComposerMessagesFinder do end it "doesn't notify a user it has already notified in this topic" do - UserHistory.create!(action: UserHistory.actions[:notified_about_dominating_topic], topic_id: topic.id, target_user_id: user.id) + UserHistory.create!( + action: UserHistory.actions[:notified_about_dominating_topic], + topic_id: topic.id, + target_user_id: user.id, + ) expect(finder.check_dominating_topic).to be_blank end it "notifies a user if the topic is different" do - UserHistory.create!(action: UserHistory.actions[:notified_about_dominating_topic], topic_id: topic.id + 1, target_user_id: user.id) + UserHistory.create!( + action: UserHistory.actions[:notified_about_dominating_topic], + topic_id: topic.id + 1, + target_user_id: user.id, + ) expect(finder.check_dominating_topic).to be_present end @@ -300,13 +324,11 @@ RSpec.describe ComposerMessagesFinder do it "creates a notified_about_dominating_topic log" do expect(UserHistory.exists_for_user?(user, :notified_about_dominating_topic)).to eq(true) end - end end - end - describe '.check_get_a_room' do + describe ".check_get_a_room" do fab!(:user) { Fabricate(:user) } fab!(:other_user) { Fabricate(:user) } fab!(:third_user) { Fabricate(:user) } @@ -317,13 +339,9 @@ RSpec.describe ComposerMessagesFinder do Fabricate(:post, topic: topic, user: third_user, reply_to_user_id: op.user_id) end - fab!(:first_reply) do - Fabricate(:post, topic: topic, user: user, reply_to_user_id: op.user_id) - end + fab!(:first_reply) { Fabricate(:post, topic: topic, user: user, reply_to_user_id: op.user_id) } - fab!(:second_reply) do - Fabricate(:post, topic: topic, user: user, reply_to_user_id: op.user_id) - end + fab!(:second_reply) { Fabricate(:post, topic: topic, user: user, reply_to_user_id: op.user_id) } before do SiteSetting.educate_until_posts = 10 @@ -332,23 +350,40 @@ RSpec.describe ComposerMessagesFinder do end it "does not show the message for new topics" do - finder = ComposerMessagesFinder.new(user, composer_action: 'createTopic') + finder = ComposerMessagesFinder.new(user, composer_action: "createTopic") expect(finder.check_get_a_room(min_users_posted: 2)).to be_blank end it "does not give a message without a topic id" do - expect(ComposerMessagesFinder.new(user, composer_action: 'reply').check_get_a_room(min_users_posted: 2)).to be_blank + expect( + ComposerMessagesFinder.new(user, composer_action: "reply").check_get_a_room( + min_users_posted: 2, + ), + ).to be_blank end it "does not give a message if the topic's category is read_restricted" do topic.category.update(read_restricted: true) - finder = ComposerMessagesFinder.new(user, composer_action: 'reply', topic_id: topic.id, post_id: op.id) + finder = + ComposerMessagesFinder.new( + user, + composer_action: "reply", + topic_id: topic.id, + post_id: op.id, + ) finder.check_get_a_room(min_users_posted: 2) expect(UserHistory.exists_for_user?(user, :notified_about_get_a_room)).to eq(false) end context "with reply" do - let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply', topic_id: topic.id, post_id: op.id) } + let(:finder) do + ComposerMessagesFinder.new( + user, + composer_action: "reply", + topic_id: topic.id, + post_id: op.id, + ) + end it "does not give a message to users who are still in the 'education' phase" do user.stubs(:post_count).returns(9) @@ -359,7 +394,7 @@ RSpec.describe ComposerMessagesFinder do UserHistory.create!( action: UserHistory.actions[:notified_about_get_a_room], target_user_id: user.id, - topic_id: topic.id + topic_id: topic.id, ) expect(finder.check_get_a_room(min_users_posted: 2)).to be_blank end @@ -368,7 +403,7 @@ RSpec.describe ComposerMessagesFinder do UserHistory.create!( action: UserHistory.actions[:notified_about_get_a_room], target_user_id: user.id, - topic_id: topic.id + 1 + topic_id: topic.id + 1, ) expect(finder.check_get_a_room(min_users_posted: 2)).to be_present end @@ -390,17 +425,18 @@ RSpec.describe ComposerMessagesFinder do end it "doesn't notify in a message" do - topic.update_columns(category_id: nil, archetype: 'private_message') + topic.update_columns(category_id: nil, archetype: "private_message") expect(finder.check_get_a_room(min_users_posted: 2)).to be_blank end it "doesn't notify when replying to a different user" do - other_finder = ComposerMessagesFinder.new( - user, - composer_action: 'reply', - topic_id: topic.id, - post_id: other_user_reply.id - ) + other_finder = + ComposerMessagesFinder.new( + user, + composer_action: "reply", + topic_id: topic.id, + post_id: other_user_reply.id, + ) expect(other_finder.check_get_a_room(min_users_posted: 2)).to be_blank end @@ -418,79 +454,94 @@ RSpec.describe ComposerMessagesFinder do it "works as expected" do expect(message).to be_present - expect(message[:id]).to eq('get_a_room') + expect(message[:id]).to eq("get_a_room") expect(message[:wait_for_typing]).to eq(true) - expect(message[:templateName]).to eq('get-a-room') + expect(message[:templateName]).to eq("get-a-room") expect(UserHistory.exists_for_user?(user, :notified_about_get_a_room)).to eq(true) end end end - end - describe '.check_reviving_old_topic' do - fab!(:user) { Fabricate(:user) } + describe ".check_reviving_old_topic" do + fab!(:user) { Fabricate(:user) } fab!(:topic) { Fabricate(:topic) } it "does not give a message without a topic id" do - expect(described_class.new(user, composer_action: 'createTopic').check_reviving_old_topic).to be_blank - expect(described_class.new(user, composer_action: 'reply').check_reviving_old_topic).to be_blank + expect( + described_class.new(user, composer_action: "createTopic").check_reviving_old_topic, + ).to be_blank + expect( + described_class.new(user, composer_action: "reply").check_reviving_old_topic, + ).to be_blank end context "with a reply" do context "when warn_reviving_old_topic_age is 180 days" do - before do - SiteSetting.warn_reviving_old_topic_age = 180 - end + before { SiteSetting.warn_reviving_old_topic_age = 180 } it "does not notify if last post is recent" do topic = Fabricate(:topic, last_posted_at: 1.hour.ago) - expect(described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank + expect( + described_class.new( + user, + composer_action: "reply", + topic_id: topic.id, + ).check_reviving_old_topic, + ).to be_blank end it "notifies if last post is old" do topic = Fabricate(:topic, last_posted_at: 181.days.ago) - message = described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic + message = + described_class.new( + user, + composer_action: "reply", + topic_id: topic.id, + ).check_reviving_old_topic expect(message).not_to be_blank expect(message[:body]).to match(/6 months ago/) end end context "when warn_reviving_old_topic_age is 0" do - before do - SiteSetting.warn_reviving_old_topic_age = 0 - end + before { SiteSetting.warn_reviving_old_topic_age = 0 } it "does not notify if last post is new" do topic = Fabricate(:topic, last_posted_at: 1.hour.ago) - expect(described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank + expect( + described_class.new( + user, + composer_action: "reply", + topic_id: topic.id, + ).check_reviving_old_topic, + ).to be_blank end it "does not notify if last post is old" do topic = Fabricate(:topic, last_posted_at: 365.days.ago) - expect(described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank + expect( + described_class.new( + user, + composer_action: "reply", + topic_id: topic.id, + ).check_reviving_old_topic, + ).to be_blank end end end end - context 'when editing a post' do + context "when editing a post" do fab!(:user) { Fabricate(:user) } fab!(:topic) { Fabricate(:post).topic } let!(:post) do - PostCreator.create!( - user, - topic_id: topic.id, - post_number: 1, - raw: 'omg my first post' - ) + PostCreator.create!(user, topic_id: topic.id, post_number: 1, raw: "omg my first post") end - let(:edit_post_finder) do - ComposerMessagesFinder.new(user, composer_action: 'edit') - end + let(:edit_post_finder) { ComposerMessagesFinder.new(user, composer_action: "edit") } before do SiteSetting.disable_avatar_education_message = true @@ -502,25 +553,28 @@ RSpec.describe ComposerMessagesFinder do end end - describe '#user_not_seen_in_a_while' do + describe "#user_not_seen_in_a_while" do fab!(:user_1) { Fabricate(:user, last_seen_at: 3.years.ago) } fab!(:user_2) { Fabricate(:user, last_seen_at: 2.years.ago) } fab!(:user_3) { Fabricate(:user, last_seen_at: 6.months.ago) } - before do - SiteSetting.pm_warn_user_last_seen_months_ago = 24 - end + before { SiteSetting.pm_warn_user_last_seen_months_ago = 24 } - it 'returns users that have not been seen recently' do - users = ComposerMessagesFinder.user_not_seen_in_a_while([user_1.username, user_2.username, user_3.username]) + it "returns users that have not been seen recently" do + users = + ComposerMessagesFinder.user_not_seen_in_a_while( + [user_1.username, user_2.username, user_3.username], + ) expect(users).to contain_exactly(user_1.username, user_2.username) end - it 'accounts for pm_warn_user_last_seen_months_ago site setting' do + it "accounts for pm_warn_user_last_seen_months_ago site setting" do SiteSetting.pm_warn_user_last_seen_months_ago = 30 - users = ComposerMessagesFinder.user_not_seen_in_a_while([user_1.username, user_2.username, user_3.username]) + users = + ComposerMessagesFinder.user_not_seen_in_a_while( + [user_1.username, user_2.username, user_3.username], + ) expect(users).to contain_exactly(user_1.username) end end - end diff --git a/spec/lib/compression/engine_spec.rb b/spec/lib/compression/engine_spec.rb index 6ca9a969ed1..87cbed2488b 100644 --- a/spec/lib/compression/engine_spec.rb +++ b/spec/lib/compression/engine_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Compression::Engine do let(:available_size) { SiteSetting.decompressed_theme_max_file_size_mb } - let(:folder_name) { 'test' } + let(:folder_name) { "test" } let(:temp_folder) do path = "#{Pathname.new(Dir.tmpdir).realpath}/#{SecureRandom.hex}" FileUtils.mkdir(path) @@ -12,30 +12,36 @@ RSpec.describe Compression::Engine do before do Dir.chdir(temp_folder) do FileUtils.mkdir_p("#{folder_name}/a") - File.write("#{folder_name}/hello.txt", 'hello world') - File.write("#{folder_name}/a/inner", 'hello world inner') + File.write("#{folder_name}/hello.txt", "hello world") + File.write("#{folder_name}/a/inner", "hello world inner") end end after { FileUtils.rm_rf(temp_folder) } - it 'raises an exception when the file is not supported' do - unknown_extension = 'a_file.crazyext' - expect { described_class.engine_for(unknown_extension) }.to raise_error Compression::Engine::UnsupportedFileExtension + it "raises an exception when the file is not supported" do + unknown_extension = "a_file.crazyext" + expect { + described_class.engine_for(unknown_extension) + }.to raise_error Compression::Engine::UnsupportedFileExtension end - describe 'compressing and decompressing files' do + describe "compressing and decompressing files" do before do Dir.chdir(temp_folder) do - @compressed_path = Compression::Engine.engine_for("#{folder_name}#{extension}").compress(temp_folder, folder_name) + @compressed_path = + Compression::Engine.engine_for("#{folder_name}#{extension}").compress( + temp_folder, + folder_name, + ) FileUtils.rm_rf("#{folder_name}/") end end - context 'when working with zip files' do - let(:extension) { '.zip' } + context "when working with zip files" do + let(:extension) { ".zip" } - it 'decompresses the folder and inspects files correctly' do + it "decompresses the folder and inspects files correctly" do engine = described_class.engine_for(@compressed_path) extract_location = "#{temp_folder}/extract_location" @@ -52,16 +58,12 @@ RSpec.describe Compression::Engine do zip_file = "#{temp_folder}/theme.zip" Zip::File.open(zip_file, create: true) do |zipfile| - zipfile.get_output_stream("child-file") do |f| - f.puts("child file") - end + zipfile.get_output_stream("child-file") { |f| f.puts("child file") } zipfile.get_output_stream("../escape-decompression-folder.txt") do |f| f.puts("file that attempts to escape the decompression destination directory") end zipfile.mkdir("child-dir") - zipfile.get_output_stream("child-dir/grandchild-file") do |f| - f.puts("grandchild file") - end + zipfile.get_output_stream("child-dir/grandchild-file") { |f| f.puts("grandchild file") } end extract_location = "#{temp_folder}/extract_location" @@ -74,7 +76,7 @@ RSpec.describe Compression::Engine do "extract_location/child-file", "extract_location/child-dir", "extract_location/child-dir/grandchild-file", - "theme.zip" + "theme.zip", ) end end @@ -97,10 +99,10 @@ RSpec.describe Compression::Engine do end end - context 'when working with .tar.gz files' do - let(:extension) { '.tar.gz' } + context "when working with .tar.gz files" do + let(:extension) { ".tar.gz" } - it 'decompresses the folder and inspects files correctly' do + it "decompresses the folder and inspects files correctly" do engine = described_class.engine_for(@compressed_path) engine.decompress(temp_folder, "#{temp_folder}/#{folder_name}.tar.gz", available_size) @@ -116,16 +118,12 @@ RSpec.describe Compression::Engine do tar_file = "#{temp_folder}/theme.tar" File.open(tar_file, "wb") do |file| Gem::Package::TarWriter.new(file) do |tar| - tar.add_file("child-file", 644) do |tf| - tf.write("child file") - end + tar.add_file("child-file", 644) { |tf| tf.write("child file") } tar.add_file("../escape-extraction-folder", 644) do |tf| tf.write("file that attempts to escape the decompression destination directory") end tar.mkdir("child-dir", 755) - tar.add_file("child-dir/grandchild-file", 644) do |tf| - tf.write("grandchild file") - end + tar.add_file("child-dir/grandchild-file", 644) { |tf| tf.write("grandchild file") } end end tar_gz_file = "#{temp_folder}/theme.tar.gz" @@ -167,10 +165,10 @@ RSpec.describe Compression::Engine do end end - context 'when working with .tar files' do - let(:extension) { '.tar' } + context "when working with .tar files" do + let(:extension) { ".tar" } - it 'decompress the folder and inspect files correctly' do + it "decompress the folder and inspect files correctly" do engine = described_class.engine_for(@compressed_path) engine.decompress(temp_folder, "#{temp_folder}/#{folder_name}.tar", available_size) diff --git a/spec/lib/concern/cached_counting_spec.rb b/spec/lib/concern/cached_counting_spec.rb index 260744902d5..041a4c3c24c 100644 --- a/spec/lib/concern/cached_counting_spec.rb +++ b/spec/lib/concern/cached_counting_spec.rb @@ -39,7 +39,6 @@ RSpec.describe CachedCounting do end it "can dispatch counts to backing class" do - CachedCounting.queue("a,a", TestCachedCounting) CachedCounting.queue("a,a", TestCachedCounting) CachedCounting.queue("b", TestCachedCounting) @@ -48,7 +47,6 @@ RSpec.describe CachedCounting do CachedCounting.flush_to_db expect(TestCachedCounting.data).to eq({ "a,a" => 2, "b" => 1 }) - end end end @@ -76,20 +74,15 @@ RSpec.describe CachedCounting do CachedCounting.enable end - after do - CachedCounting.disable - end + after { CachedCounting.disable } it "can dispatch data via background thread" do - freeze_time d1 = Time.now.utc.to_date RailsCacheCounter.perform_increment!("a,a") RailsCacheCounter.perform_increment!("b") - 20.times do - RailsCacheCounter.perform_increment!("a,a") - end + 20.times { RailsCacheCounter.perform_increment!("a,a") } freeze_time 2.days.from_now d2 = Time.now.utc.to_date @@ -99,12 +92,7 @@ RSpec.describe CachedCounting do CachedCounting.flush - expected = { - ["a,a", d1] => 21, - ["b", d1] => 1, - ["a,a", d2] => 1, - ["d", d2] => 1, - } + expected = { ["a,a", d1] => 21, ["b", d1] => 1, ["a,a", d2] => 1, ["d", d2] => 1 } expect(RailsCacheCounter.cache_data).to eq(expected) end diff --git a/spec/lib/concern/category_hashtag_spec.rb b/spec/lib/concern/category_hashtag_spec.rb index 57d687beb20..9a815653d5d 100644 --- a/spec/lib/concern/category_hashtag_spec.rb +++ b/spec/lib/concern/category_hashtag_spec.rb @@ -1,18 +1,20 @@ # frozen_string_literal: true RSpec.describe CategoryHashtag do - describe '#query_from_hashtag_slug' do + describe "#query_from_hashtag_slug" do fab!(:parent_category) { Fabricate(:category) } fab!(:child_category) { Fabricate(:category, parent_category: parent_category) } it "should return the right result for a parent category slug" do - expect(Category.query_from_hashtag_slug(parent_category.slug)) - .to eq(parent_category) + expect(Category.query_from_hashtag_slug(parent_category.slug)).to eq(parent_category) end it "should return the right result for a parent and child category slug" do - expect(Category.query_from_hashtag_slug("#{parent_category.slug}#{CategoryHashtag::SEPARATOR}#{child_category.slug}")) - .to eq(child_category) + expect( + Category.query_from_hashtag_slug( + "#{parent_category.slug}#{CategoryHashtag::SEPARATOR}#{child_category.slug}", + ), + ).to eq(child_category) end it "should return nil for incorrect parent category slug" do @@ -20,22 +22,29 @@ RSpec.describe CategoryHashtag do end it "should return nil for incorrect parent and child category slug" do - expect(Category.query_from_hashtag_slug("random-slug#{CategoryHashtag::SEPARATOR}random-slug")).to eq(nil) + expect( + Category.query_from_hashtag_slug("random-slug#{CategoryHashtag::SEPARATOR}random-slug"), + ).to eq(nil) end it "should return nil for a non-existent root and a parent subcategory" do - expect(Category.query_from_hashtag_slug("non-existent#{CategoryHashtag::SEPARATOR}#{parent_category.slug}")).to eq(nil) + expect( + Category.query_from_hashtag_slug( + "non-existent#{CategoryHashtag::SEPARATOR}#{parent_category.slug}", + ), + ).to eq(nil) end context "with multi-level categories" do - before do - SiteSetting.max_category_nesting = 3 - end + before { SiteSetting.max_category_nesting = 3 } it "should return the right result for a grand child category slug" do category = Fabricate(:category, parent_category: child_category) - expect(Category.query_from_hashtag_slug("#{child_category.slug}#{CategoryHashtag::SEPARATOR}#{category.slug}")) - .to eq(category) + expect( + Category.query_from_hashtag_slug( + "#{child_category.slug}#{CategoryHashtag::SEPARATOR}#{category.slug}", + ), + ).to eq(category) end end end diff --git a/spec/lib/concern/has_custom_fields_spec.rb b/spec/lib/concern/has_custom_fields_spec.rb index af0bbaebd81..0557193c61f 100644 --- a/spec/lib/concern/has_custom_fields_spec.rb +++ b/spec/lib/concern/has_custom_fields_spec.rb @@ -4,7 +4,9 @@ RSpec.describe HasCustomFields do describe "custom_fields" do before do DB.exec("create temporary table custom_fields_test_items(id SERIAL primary key)") - DB.exec("create temporary table custom_fields_test_item_custom_fields(id SERIAL primary key, custom_fields_test_item_id int, name varchar(256) not null, value text, created_at TIMESTAMP, updated_at TIMESTAMP)") + DB.exec( + "create temporary table custom_fields_test_item_custom_fields(id SERIAL primary key, custom_fields_test_item_id int, name varchar(256) not null, value text, created_at TIMESTAMP, updated_at TIMESTAMP)", + ) DB.exec(<<~SQL) CREATE UNIQUE INDEX ON custom_fields_test_item_custom_fields (custom_fields_test_item_id) WHERE NAME = 'rare' @@ -38,7 +40,9 @@ RSpec.describe HasCustomFields do it "errors if a custom field is not preloaded" do test_item = CustomFieldsTestItem.new CustomFieldsTestItem.preload_custom_fields([test_item], ["test_field"]) - expect { test_item.custom_fields["other_field"] }.to raise_error(HasCustomFields::NotPreloadedError) + expect { test_item.custom_fields["other_field"] }.to raise_error( + HasCustomFields::NotPreloadedError, + ) end it "resets the preloaded_custom_fields if preload_custom_fields is called twice" do @@ -105,7 +109,10 @@ RSpec.describe HasCustomFields do # should be casted right after saving expect(test_item.custom_fields["a"]).to eq("0") - DB.exec("UPDATE custom_fields_test_item_custom_fields SET value='1' WHERE custom_fields_test_item_id=? AND name='a'", test_item.id) + DB.exec( + "UPDATE custom_fields_test_item_custom_fields SET value='1' WHERE custom_fields_test_item_id=? AND name='a'", + test_item.id, + ) # still the same, did not load expect(test_item.custom_fields["a"]).to eq("0") @@ -137,25 +144,25 @@ RSpec.describe HasCustomFields do expect(db_item.custom_fields).to eq("array" => [1]) test_item = CustomFieldsTestItem.new - test_item.custom_fields = { "a" => ["b", "c", "d"] } + test_item.custom_fields = { "a" => %w[b c d] } test_item.save db_item = CustomFieldsTestItem.find(test_item.id) - expect(db_item.custom_fields).to eq("a" => ["b", "c", "d"]) + expect(db_item.custom_fields).to eq("a" => %w[b c d]) - db_item.custom_fields.update('a' => ['c', 'd']) + db_item.custom_fields.update("a" => %w[c d]) db_item.save - expect(db_item.custom_fields).to eq("a" => ["c", "d"]) + expect(db_item.custom_fields).to eq("a" => %w[c d]) # It can be updated to the exact same value - db_item.custom_fields.update('a' => ['c']) + db_item.custom_fields.update("a" => ["c"]) db_item.save expect(db_item.custom_fields).to eq("a" => "c") - db_item.custom_fields.update('a' => ['c']) + db_item.custom_fields.update("a" => ["c"]) db_item.save expect(db_item.custom_fields).to eq("a" => "c") - db_item.custom_fields.delete('a') + db_item.custom_fields.delete("a") expect(db_item.custom_fields).to eq({}) end @@ -176,10 +183,10 @@ RSpec.describe HasCustomFields do test_item = CustomFieldsTestItem.new test_item.custom_fields = { "a" => ["b", 10, "d"] } test_item.save - expect(test_item.custom_fields).to eq("a" => ["b", "10", "d"]) + expect(test_item.custom_fields).to eq("a" => %w[b 10 d]) db_item = CustomFieldsTestItem.find(test_item.id) - expect(db_item.custom_fields).to eq("a" => ["b", "10", "d"]) + expect(db_item.custom_fields).to eq("a" => %w[b 10 d]) end it "supports type coercion" do @@ -192,14 +199,22 @@ RSpec.describe HasCustomFields do test_item.save test_item.reload - expect(test_item.custom_fields).to eq("bool" => true, "int" => 1, "json" => { "foo" => "bar" }) + expect(test_item.custom_fields).to eq( + "bool" => true, + "int" => 1, + "json" => { + "foo" => "bar", + }, + ) - before_ids = CustomFieldsTestItemCustomField.where(custom_fields_test_item_id: test_item.id).pluck(:id) + before_ids = + CustomFieldsTestItemCustomField.where(custom_fields_test_item_id: test_item.id).pluck(:id) test_item.custom_fields["bool"] = false test_item.save - after_ids = CustomFieldsTestItemCustomField.where(custom_fields_test_item_id: test_item.id).pluck(:id) + after_ids = + CustomFieldsTestItemCustomField.where(custom_fields_test_item_id: test_item.id).pluck(:id) # we updated only 1 custom field, so there should be only 1 different id expect((before_ids - after_ids).size).to eq(1) @@ -234,23 +249,19 @@ RSpec.describe HasCustomFields do CustomFieldsTestItem.register_custom_field_type(field_type, :json) item = CustomFieldsTestItem.new - item.custom_fields = { - "json_array" => [{ a: "test" }, { b: "another" }] - } + item.custom_fields = { "json_array" => [{ a: "test" }, { b: "another" }] } item.save item.reload - expect(item.custom_fields[field_type]).to eq( - [{ "a" => "test" }, { "b" => "another" }] - ) + expect(item.custom_fields[field_type]).to eq([{ "a" => "test" }, { "b" => "another" }]) - item.custom_fields["json_array"] = ['a', 'b'] + item.custom_fields["json_array"] = %w[a b] item.save item.reload - expect(item.custom_fields[field_type]).to eq(["a", "b"]) + expect(item.custom_fields[field_type]).to eq(%w[a b]) end it "will not fail to load custom fields if json is corrupt" do @@ -262,7 +273,7 @@ RSpec.describe HasCustomFields do CustomFieldsTestItemCustomField.create!( custom_fields_test_item_id: item.id, name: field_type, - value: "{test" + value: "{test", ) item = item.reload @@ -271,34 +282,34 @@ RSpec.describe HasCustomFields do it "supports bulk retrieval with a list of ids" do item1 = CustomFieldsTestItem.new - item1.custom_fields = { "a" => ["b", "c", "d"], 'not_allowlisted' => 'secret' } + item1.custom_fields = { "a" => %w[b c d], "not_allowlisted" => "secret" } item1.save item2 = CustomFieldsTestItem.new - item2.custom_fields = { "e" => 'hallo' } + item2.custom_fields = { "e" => "hallo" } item2.save - fields = CustomFieldsTestItem.custom_fields_for_ids([item1.id, item2.id], ['a', 'e']) + fields = CustomFieldsTestItem.custom_fields_for_ids([item1.id, item2.id], %w[a e]) expect(fields).to be_present - expect(fields[item1.id]['a']).to match_array(['b', 'c', 'd']) - expect(fields[item1.id]['not_allowlisted']).to be_blank - expect(fields[item2.id]['e']).to eq('hallo') + expect(fields[item1.id]["a"]).to match_array(%w[b c d]) + expect(fields[item1.id]["not_allowlisted"]).to be_blank + expect(fields[item2.id]["e"]).to eq("hallo") end it "handles interleaving saving properly" do - field_type = 'deep-nest-test' + field_type = "deep-nest-test" CustomFieldsTestItem.register_custom_field_type(field_type, :json) test_item = CustomFieldsTestItem.create! test_item.custom_fields[field_type] ||= {} - test_item.custom_fields[field_type]['b'] ||= {} - test_item.custom_fields[field_type]['b']['c'] = 'd' + test_item.custom_fields[field_type]["b"] ||= {} + test_item.custom_fields[field_type]["b"]["c"] = "d" test_item.save_custom_fields(true) db_item = CustomFieldsTestItem.find(test_item.id) - db_item.custom_fields[field_type]['b']['e'] = 'f' - test_item.custom_fields[field_type]['b']['e'] = 'f' - expected = { field_type => { 'b' => { 'c' => 'd', 'e' => 'f' } } } + db_item.custom_fields[field_type]["b"]["e"] = "f" + test_item.custom_fields[field_type]["b"]["e"] = "f" + expected = { field_type => { "b" => { "c" => "d", "e" => "f" } } } db_item.save_custom_fields(true) expect(db_item.reload.custom_fields).to eq(expected) @@ -307,7 +318,7 @@ RSpec.describe HasCustomFields do expect(test_item.reload.custom_fields).to eq(expected) end - it 'determines clean state correctly for mutable fields' do + it "determines clean state correctly for mutable fields" do json_field = "json_field" array_field = "array_field" CustomFieldsTestItem.register_custom_field_type(json_field, :json) @@ -339,94 +350,94 @@ RSpec.describe HasCustomFields do describe "create_singular" do it "creates new records" do item = CustomFieldsTestItem.create! - item.create_singular('hello', 'world') - expect(item.reload.custom_fields['hello']).to eq('world') + item.create_singular("hello", "world") + expect(item.reload.custom_fields["hello"]).to eq("world") end it "upserts on a database constraint error" do item0 = CustomFieldsTestItem.new item0.custom_fields = { "rare" => "gem" } item0.save - expect(item0.reload.custom_fields['rare']).to eq("gem") + expect(item0.reload.custom_fields["rare"]).to eq("gem") - item0.create_singular('rare', "diamond") - expect(item0.reload.custom_fields['rare']).to eq("diamond") + item0.create_singular("rare", "diamond") + expect(item0.reload.custom_fields["rare"]).to eq("diamond") end end describe "upsert_custom_fields" do - it 'upserts records' do + it "upserts records" do test_item = CustomFieldsTestItem.create - test_item.upsert_custom_fields('hello' => 'world', 'abc' => 'def') + test_item.upsert_custom_fields("hello" => "world", "abc" => "def") # In memory - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('def') + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("def") # Persisted test_item.reload - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('def') + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("def") # In memory - test_item.upsert_custom_fields('abc' => 'ghi') - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('ghi') + test_item.upsert_custom_fields("abc" => "ghi") + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("ghi") # Persisted test_item.reload - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('ghi') + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("ghi") end - it 'allows upsert to use keywords' do + it "allows upsert to use keywords" do test_item = CustomFieldsTestItem.create - test_item.upsert_custom_fields(hello: 'world', abc: 'def') + test_item.upsert_custom_fields(hello: "world", abc: "def") # In memory - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('def') + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("def") # Persisted test_item.reload - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('def') + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("def") # In memory - test_item.upsert_custom_fields('abc' => 'ghi') - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('ghi') + test_item.upsert_custom_fields("abc" => "ghi") + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("ghi") # Persisted test_item.reload - expect(test_item.custom_fields['hello']).to eq('world') - expect(test_item.custom_fields['abc']).to eq('ghi') + expect(test_item.custom_fields["hello"]).to eq("world") + expect(test_item.custom_fields["abc"]).to eq("ghi") end - it 'allows using string and symbol indices interchangeably' do + it "allows using string and symbol indices interchangeably" do test_item = CustomFieldsTestItem.new test_item.custom_fields["bob"] = "marley" test_item.custom_fields["jack"] = "black" # In memory - expect(test_item.custom_fields[:bob]).to eq('marley') - expect(test_item.custom_fields[:jack]).to eq('black') + expect(test_item.custom_fields[:bob]).to eq("marley") + expect(test_item.custom_fields[:jack]).to eq("black") # Persisted test_item.save test_item.reload - expect(test_item.custom_fields[:bob]).to eq('marley') - expect(test_item.custom_fields[:jack]).to eq('black') + expect(test_item.custom_fields[:bob]).to eq("marley") + expect(test_item.custom_fields[:jack]).to eq("black") # Update via string index again - test_item.custom_fields['bob'] = 'the builder' + test_item.custom_fields["bob"] = "the builder" - expect(test_item.custom_fields[:bob]).to eq('the builder') + expect(test_item.custom_fields[:bob]).to eq("the builder") test_item.save test_item.reload - expect(test_item.custom_fields[:bob]).to eq('the builder') + expect(test_item.custom_fields[:bob]).to eq("the builder") end end end diff --git a/spec/lib/concern/has_search_data_spec.rb b/spec/lib/concern/has_search_data_spec.rb index 74f4fbb66d3..b7e1f579421 100644 --- a/spec/lib/concern/has_search_data_spec.rb +++ b/spec/lib/concern/has_search_data_spec.rb @@ -4,7 +4,9 @@ RSpec.describe HasSearchData do describe "belongs to its model" do before do DB.exec("create temporary table model_items(id SERIAL primary key)") - DB.exec("create temporary table model_item_search_data(model_item_id int primary key, search_data tsvector, raw_data text, locale text)") + DB.exec( + "create temporary table model_item_search_data(model_item_id int primary key, search_data tsvector, raw_data text, locale text)", + ) class ModelItem < ActiveRecord::Base has_one :model_item_search_data, dependent: :destroy @@ -29,17 +31,18 @@ RSpec.describe HasSearchData do item = ModelItem.create! item.create_model_item_search_data!( model_item_id: item.id, - search_data: 'a', - raw_data: 'a', - locale: 'en') + search_data: "a", + raw_data: "a", + locale: "en", + ) item end - it 'sets its primary key into associated model' do - expect(ModelItemSearchData.primary_key).to eq 'model_item_id' + it "sets its primary key into associated model" do + expect(ModelItemSearchData.primary_key).to eq "model_item_id" end - it 'can access the model' do + it "can access the model" do record_id = item.id expect(ModelItemSearchData.find_by(model_item_id: record_id).model_item_id).to eq record_id end diff --git a/spec/lib/concern/positionable_spec.rb b/spec/lib/concern/positionable_spec.rb index f4a57463867..7beb1298201 100644 --- a/spec/lib/concern/positionable_spec.rb +++ b/spec/lib/concern/positionable_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Positionable do def positions - TestItem.order('position asc, id asc').pluck(:id) + TestItem.order("position asc, id asc").pluck(:id) end describe "move_to" do @@ -23,9 +23,7 @@ RSpec.describe Positionable do end it "can position stuff correctly" do - 5.times do |i| - DB.exec("insert into test_items(id,position) values(#{i}, #{i})") - end + 5.times { |i| DB.exec("insert into test_items(id,position) values(#{i}, #{i})") } expect(positions).to eq([0, 1, 2, 3, 4]) TestItem.find(3).move_to(0) diff --git a/spec/lib/concern/searchable_spec.rb b/spec/lib/concern/searchable_spec.rb index b92b0eb4af8..22bb1b5fc2c 100644 --- a/spec/lib/concern/searchable_spec.rb +++ b/spec/lib/concern/searchable_spec.rb @@ -4,14 +4,16 @@ RSpec.describe Searchable do describe "has search data" do before do DB.exec("create temporary table searchable_records(id SERIAL primary key)") - DB.exec("create temporary table searchable_record_search_data(searchable_record_id int primary key, search_data tsvector, raw_data text, locale text)") + DB.exec( + "create temporary table searchable_record_search_data(searchable_record_id int primary key, search_data tsvector, raw_data text, locale text)", + ) class SearchableRecord < ActiveRecord::Base include Searchable end class SearchableRecordSearchData < ActiveRecord::Base - self.primary_key = 'searchable_record_id' + self.primary_key = "searchable_record_id" belongs_to :test_item end end @@ -28,26 +30,20 @@ RSpec.describe Searchable do let(:item) { SearchableRecord.create! } - it 'can build the data' do + it "can build the data" do expect(item.build_searchable_record_search_data).to be_truthy end - it 'can save the data' do - item.build_searchable_record_search_data( - search_data: '', - raw_data: 'a', - locale: 'en') + it "can save the data" do + item.build_searchable_record_search_data(search_data: "", raw_data: "a", locale: "en") item.save loaded = SearchableRecord.find(item.id) - expect(loaded.searchable_record_search_data.raw_data).to eq 'a' + expect(loaded.searchable_record_search_data.raw_data).to eq "a" end - it 'destroy the search data when the item is deprived' do - item.build_searchable_record_search_data( - search_data: '', - raw_data: 'a', - locale: 'en') + it "destroy the search data when the item is deprived" do + item.build_searchable_record_search_data(search_data: "", raw_data: "a", locale: "en") item.save item_id = item.id item.destroy diff --git a/spec/lib/concern/second_factor_manager_spec.rb b/spec/lib/concern/second_factor_manager_spec.rb index a30b726e731..80dfeb67a43 100644 --- a/spec/lib/concern/second_factor_manager_spec.rb +++ b/spec/lib/concern/second_factor_manager_spec.rb @@ -8,29 +8,31 @@ RSpec.describe SecondFactorManager do :user_security_key, user: user, public_key: valid_security_key_data[:public_key], - credential_id: valid_security_key_data[:credential_id] + credential_id: valid_security_key_data[:credential_id], ) end fab!(:another_user) { Fabricate(:user) } fab!(:user_second_factor_backup) { Fabricate(:user_second_factor_backup) } - let(:user_backup) { user_second_factor_backup.user } + let(:user_backup) { user_second_factor_backup.user } - describe '#totp' do - it 'should return the right data' do + describe "#totp" do + it "should return the right data" do totp = nil - expect do - totp = another_user.create_totp(enabled: true) - end.to change { UserSecondFactor.count }.by(1) + expect do totp = another_user.create_totp(enabled: true) end.to change { + UserSecondFactor.count + }.by(1) expect(totp.totp_object.issuer).to eq(SiteSetting.title) - expect(totp.totp_object.secret).to eq(another_user.reload.user_second_factors.totps.first.data) + expect(totp.totp_object.secret).to eq( + another_user.reload.user_second_factors.totps.first.data, + ) end end - describe '#create_totp' do - it 'should create the right record' do + describe "#create_totp" do + it "should create the right record" do second_factor = another_user.create_totp(enabled: true) expect(second_factor.method).to eq(UserSecondFactor.methods[:totp]) @@ -39,28 +41,28 @@ RSpec.describe SecondFactorManager do end end - describe '#totp_provisioning_uri' do - it 'should return the right uri' do + describe "#totp_provisioning_uri" do + it "should return the right uri" do expect(user.user_second_factors.totps.first.totp_provisioning_uri).to eq( - "otpauth://totp/#{SiteSetting.title}:#{ERB::Util.url_encode(user.email)}?secret=#{user_second_factor_totp.data}&issuer=#{SiteSetting.title}" + "otpauth://totp/#{SiteSetting.title}:#{ERB::Util.url_encode(user.email)}?secret=#{user_second_factor_totp.data}&issuer=#{SiteSetting.title}", ) end - it 'should handle a colon in the site title' do - SiteSetting.title = 'Spaceballs: The Discourse' + it "should handle a colon in the site title" do + SiteSetting.title = "Spaceballs: The Discourse" expect(user.user_second_factors.totps.first.totp_provisioning_uri).to eq( - "otpauth://totp/Spaceballs%20The%20Discourse:#{ERB::Util.url_encode(user.email)}?secret=#{user_second_factor_totp.data}&issuer=Spaceballs%20The%20Discourse" + "otpauth://totp/Spaceballs%20The%20Discourse:#{ERB::Util.url_encode(user.email)}?secret=#{user_second_factor_totp.data}&issuer=Spaceballs%20The%20Discourse", ) end - it 'should handle a two words before a colon in the title' do - SiteSetting.title = 'Our Spaceballs: The Discourse' + it "should handle a two words before a colon in the title" do + SiteSetting.title = "Our Spaceballs: The Discourse" expect(user.user_second_factors.totps.first.totp_provisioning_uri).to eq( - "otpauth://totp/Our%20Spaceballs%20The%20Discourse:#{ERB::Util.url_encode(user.email)}?secret=#{user_second_factor_totp.data}&issuer=Our%20Spaceballs%20The%20Discourse" + "otpauth://totp/Our%20Spaceballs%20The%20Discourse:#{ERB::Util.url_encode(user.email)}?secret=#{user_second_factor_totp.data}&issuer=Our%20Spaceballs%20The%20Discourse", ) end end - describe '#authenticate_totp' do - it 'should be able to authenticate a token' do + describe "#authenticate_totp" do + it "should be able to authenticate a token" do freeze_time do expect(user.user_second_factors.totps.first.last_used).to eq(nil) @@ -72,52 +74,52 @@ RSpec.describe SecondFactorManager do end end - describe 'when token is blank' do - it 'should be false' do + describe "when token is blank" do + it "should be false" do expect(user.authenticate_totp(nil)).to eq(false) expect(user.user_second_factors.totps.first.last_used).to eq(nil) end end - describe 'when token is invalid' do - it 'should be false' do - expect(user.authenticate_totp('111111')).to eq(false) + describe "when token is invalid" do + it "should be false" do + expect(user.authenticate_totp("111111")).to eq(false) expect(user.user_second_factors.totps.first.last_used).to eq(nil) end end end - describe '#totp_enabled?' do - describe 'when user does not have a second factor record' do - it 'should return false' do + describe "#totp_enabled?" do + describe "when user does not have a second factor record" do + it "should return false" do expect(another_user.totp_enabled?).to eq(false) end end describe "when user's second factor record is disabled" do - it 'should return false' do + it "should return false" do disable_totp expect(user.totp_enabled?).to eq(false) end end describe "when user's second factor record is enabled" do - it 'should return true' do + it "should return true" do expect(user.totp_enabled?).to eq(true) end end - describe 'when SSO is enabled' do - it 'should return false' do - SiteSetting.discourse_connect_url = 'http://someurl.com' + describe "when SSO is enabled" do + it "should return false" do + SiteSetting.discourse_connect_url = "http://someurl.com" SiteSetting.enable_discourse_connect = true expect(user.totp_enabled?).to eq(false) end end - describe 'when local login is disabled' do - it 'should return false' do + describe "when local login is disabled" do + it "should return false" do SiteSetting.enable_local_logins = false expect(user.totp_enabled?).to eq(false) @@ -166,9 +168,7 @@ RSpec.describe SecondFactorManager do let(:secure_session) { {} } context "when neither security keys nor totp/backup codes are enabled" do - before do - disable_security_key && disable_totp - end + before { disable_security_key && disable_totp } it "returns OK, because it doesn't need to authenticate" do expect(user.authenticate_second_factor(params, secure_session).ok).to eq(true) end @@ -186,13 +186,20 @@ RSpec.describe SecondFactorManager do end context "when security key params are valid" do - let(:params) { { second_factor_token: valid_security_key_auth_post_data, second_factor_method: UserSecondFactor.methods[:security_key] } } + let(:params) do + { + second_factor_token: valid_security_key_auth_post_data, + second_factor_method: UserSecondFactor.methods[:security_key], + } + end it "returns OK" do expect(user.authenticate_second_factor(params, secure_session).ok).to eq(true) end it "sets used_2fa_method to security keys" do - expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq(UserSecondFactor.methods[:security_key]) + expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq( + UserSecondFactor.methods[:security_key], + ) end end @@ -200,12 +207,12 @@ RSpec.describe SecondFactorManager do let(:params) do { second_factor_token: { - signature: 'bad', - clientData: 'bad', - authenticatorData: 'bad', - credentialId: 'bad' + signature: "bad", + clientData: "bad", + authenticatorData: "bad", + credentialId: "bad", }, - second_factor_method: UserSecondFactor.methods[:security_key] + second_factor_method: UserSecondFactor.methods[:security_key], } end it "returns not OK" do @@ -218,15 +225,13 @@ RSpec.describe SecondFactorManager do end context "when only totp is enabled" do - before do - disable_security_key - end + before { disable_security_key } context "when totp is valid" do let(:params) do { second_factor_token: user.user_second_factors.totps.first.totp_object.now, - second_factor_method: UserSecondFactor.methods[:totp] + second_factor_method: UserSecondFactor.methods[:totp], } end it "returns OK" do @@ -234,16 +239,15 @@ RSpec.describe SecondFactorManager do end it "sets used_2fa_method to totp" do - expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq(UserSecondFactor.methods[:totp]) + expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq( + UserSecondFactor.methods[:totp], + ) end end context "when totp is invalid" do let(:params) do - { - second_factor_token: "blah", - second_factor_method: UserSecondFactor.methods[:totp] - } + { second_factor_token: "blah", second_factor_method: UserSecondFactor.methods[:totp] } end it "returns not OK" do result = user.authenticate_second_factor(params, secure_session) @@ -277,26 +281,21 @@ RSpec.describe SecondFactorManager do let(:token) { user.user_second_factors.totps.first.totp_object.now } context "when totp params are provided" do - let(:params) do - { - second_factor_token: token, - second_factor_method: method - } - end + let(:params) { { second_factor_token: token, second_factor_method: method } } it "validates totp OK" do expect(user.authenticate_second_factor(params, secure_session).ok).to eq(true) end it "sets used_2fa_method to totp" do - expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq(UserSecondFactor.methods[:totp]) + expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq( + UserSecondFactor.methods[:totp], + ) end context "when the user does not have TOTP enabled" do - let(:token) { 'test' } - before do - user.totps.destroy_all - end + let(:token) { "test" } + before { user.totps.destroy_all } it "returns an error" do result = user.authenticate_second_factor(params, secure_session) @@ -317,19 +316,21 @@ RSpec.describe SecondFactorManager do end context "when security key params are valid" do - let(:params) { { second_factor_token: valid_security_key_auth_post_data, second_factor_method: method } } + let(:params) do + { second_factor_token: valid_security_key_auth_post_data, second_factor_method: method } + end it "returns OK" do expect(user.authenticate_second_factor(params, secure_session).ok).to eq(true) end it "sets used_2fa_method to security keys" do - expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq(UserSecondFactor.methods[:security_key]) + expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq( + UserSecondFactor.methods[:security_key], + ) end context "when the user does not have security keys enabled" do - before do - user.security_keys.destroy_all - end + before { user.security_keys.destroy_all } it "returns an error" do result = user.authenticate_second_factor(params, secure_session) @@ -347,10 +348,7 @@ RSpec.describe SecondFactorManager do context "when backup code params are provided" do let(:params) do - { - second_factor_token: 'iAmValidBackupCode', - second_factor_method: method - } + { second_factor_token: "iAmValidBackupCode", second_factor_method: method } end context "when backup codes enabled" do @@ -359,14 +357,14 @@ RSpec.describe SecondFactorManager do end it "sets used_2fa_method to backup codes" do - expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq(UserSecondFactor.methods[:backup_codes]) + expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq( + UserSecondFactor.methods[:backup_codes], + ) end end context "when backup codes disabled" do - before do - user.user_second_factors.backup_codes.destroy_all - end + before { user.user_second_factors.backup_codes.destroy_all } it "returns an error" do result = user.authenticate_second_factor(params, secure_session) @@ -379,14 +377,21 @@ RSpec.describe SecondFactorManager do end context "when no totp params are provided" do - let(:params) { { second_factor_token: valid_security_key_auth_post_data, second_factor_method: UserSecondFactor.methods[:security_key] } } + let(:params) do + { + second_factor_token: valid_security_key_auth_post_data, + second_factor_method: UserSecondFactor.methods[:security_key], + } + end it "validates the security key OK" do expect(user.authenticate_second_factor(params, secure_session).ok).to eq(true) end it "sets used_2fa_method to security keys" do - expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq(UserSecondFactor.methods[:security_key]) + expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq( + UserSecondFactor.methods[:security_key], + ) end end @@ -394,7 +399,7 @@ RSpec.describe SecondFactorManager do let(:params) do { second_factor_token: user.user_second_factors.totps.first.totp_object.now, - second_factor_method: UserSecondFactor.methods[:totp] + second_factor_method: UserSecondFactor.methods[:totp], } end @@ -403,26 +408,30 @@ RSpec.describe SecondFactorManager do end it "sets used_2fa_method to totp" do - expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq(UserSecondFactor.methods[:totp]) + expect(user.authenticate_second_factor(params, secure_session).used_2fa_method).to eq( + UserSecondFactor.methods[:totp], + ) end end end end - describe 'backup codes' do - describe '#generate_backup_codes' do - it 'should generate and store 10 backup codes' do + describe "backup codes" do + describe "#generate_backup_codes" do + it "should generate and store 10 backup codes" do backup_codes = user.generate_backup_codes expect(backup_codes.length).to be 10 expect(user_backup.user_second_factors.backup_codes).to be_present - expect(user_backup.user_second_factors.backup_codes.pluck(:method).uniq[0]).to eq(UserSecondFactor.methods[:backup_codes]) + expect(user_backup.user_second_factors.backup_codes.pluck(:method).uniq[0]).to eq( + UserSecondFactor.methods[:backup_codes], + ) expect(user_backup.user_second_factors.backup_codes.pluck(:enabled).uniq[0]).to eq(true) end end - describe '#create_backup_codes' do - it 'should create 10 backup code records' do + describe "#create_backup_codes" do + it "should create 10 backup code records" do raw_codes = Array.new(10) { SecureRandom.hex(8) } backup_codes = another_user.create_backup_codes(raw_codes) @@ -430,58 +439,58 @@ RSpec.describe SecondFactorManager do end end - describe '#authenticate_backup_code' do - it 'should be able to authenticate a backup code' do + describe "#authenticate_backup_code" do + it "should be able to authenticate a backup code" do backup_code = "iAmValidBackupCode" expect(user_backup.authenticate_backup_code(backup_code)).to eq(true) expect(user_backup.authenticate_backup_code(backup_code)).to eq(false) end - describe 'when code is blank' do - it 'should be false' do + describe "when code is blank" do + it "should be false" do expect(user_backup.authenticate_backup_code(nil)).to eq(false) end end - describe 'when code is invalid' do - it 'should be false' do + describe "when code is invalid" do + it "should be false" do expect(user_backup.authenticate_backup_code("notValidBackupCode")).to eq(false) end end end - describe '#backup_codes_enabled?' do - describe 'when user does not have a second factor backup enabled' do - it 'should return false' do + describe "#backup_codes_enabled?" do + describe "when user does not have a second factor backup enabled" do + it "should return false" do expect(another_user.backup_codes_enabled?).to eq(false) end end describe "when user's second factor backup codes have been used" do - it 'should return false' do + it "should return false" do user_backup.user_second_factors.backup_codes.update_all(enabled: false) expect(user_backup.backup_codes_enabled?).to eq(false) end end describe "when user's second factor code is available" do - it 'should return true' do + it "should return true" do expect(user_backup.backup_codes_enabled?).to eq(true) end end - describe 'when SSO is enabled' do - it 'should return false' do - SiteSetting.discourse_connect_url = 'http://someurl.com' + describe "when SSO is enabled" do + it "should return false" do + SiteSetting.discourse_connect_url = "http://someurl.com" SiteSetting.enable_discourse_connect = true expect(user_backup.backup_codes_enabled?).to eq(false) end end - describe 'when local login is disabled' do - it 'should return false' do + describe "when local login is disabled" do + it "should return false" do SiteSetting.enable_local_logins = false expect(user_backup.backup_codes_enabled?).to eq(false) diff --git a/spec/lib/content_buffer_spec.rb b/spec/lib/content_buffer_spec.rb index efc8ffe0053..567f808b6e5 100644 --- a/spec/lib/content_buffer_spec.rb +++ b/spec/lib/content_buffer_spec.rb @@ -1,9 +1,8 @@ # frozen_string_literal: true -require 'content_buffer' +require "content_buffer" RSpec.describe ContentBuffer do - it "handles deletion across lines properly" do c = ContentBuffer.new("a\nbc\nc") c.apply_transform!(start: { row: 0, col: 0 }, finish: { col: 1, row: 1 }, operation: :delete) @@ -26,5 +25,4 @@ RSpec.describe ContentBuffer do c.apply_transform!(start: { row: 0, col: 5 }, operation: :insert, text: "\nworld") expect(c.to_s).to eq("hello\nworld!") end - end diff --git a/spec/lib/content_security_policy/builder_spec.rb b/spec/lib/content_security_policy/builder_spec.rb index fd0ad5ba103..ec58256d2ab 100644 --- a/spec/lib/content_security_policy/builder_spec.rb +++ b/spec/lib/content_security_policy/builder_spec.rb @@ -2,29 +2,29 @@ RSpec.describe ContentSecurityPolicy::Builder do let(:builder) { described_class.new(base_url: Discourse.base_url) } - describe '#<<' do - it 'normalizes directive name' do + describe "#<<" do + it "normalizes directive name" do builder << { - script_src: ['symbol_underscore'], - 'script-src': ['symbol_dash'], - 'script_src' => ['string_underscore'], - 'script-src' => ['string_dash'], + :script_src => ["symbol_underscore"], + :"script-src" => ["symbol_dash"], + "script_src" => ["string_underscore"], + "script-src" => ["string_dash"], } - script_srcs = parse(builder.build)['script-src'] + script_srcs = parse(builder.build)["script-src"] - expect(script_srcs).to include(*%w[symbol_underscore symbol_dash string_underscore symbol_underscore]) + expect(script_srcs).to include( + *%w[symbol_underscore symbol_dash string_underscore symbol_underscore], + ) end - it 'rejects invalid directives and ones that are not allowed to be extended' do - builder << { - invalid_src: ['invalid'], - } + it "rejects invalid directives and ones that are not allowed to be extended" do + builder << { invalid_src: ["invalid"] } - expect(builder.build).to_not include('invalid') + expect(builder.build).to_not include("invalid") end - it 'no-ops on invalid values' do + it "no-ops on invalid values" do previous = builder.build builder << nil @@ -38,9 +38,12 @@ RSpec.describe ContentSecurityPolicy::Builder do end def parse(csp_string) - csp_string.split(';').map do |policy| - directive, *sources = policy.split - [directive, sources] - end.to_h + csp_string + .split(";") + .map do |policy| + directive, *sources = policy.split + [directive, sources] + end + .to_h end end diff --git a/spec/lib/content_security_policy_spec.rb b/spec/lib/content_security_policy_spec.rb index 46f11f63be6..2d36edfbdcf 100644 --- a/spec/lib/content_security_policy_spec.rb +++ b/spec/lib/content_security_policy_spec.rb @@ -1,223 +1,231 @@ # frozen_string_literal: true RSpec.describe ContentSecurityPolicy do - after do - DiscoursePluginRegistry.reset! - end + after { DiscoursePluginRegistry.reset! } - describe 'report-uri' do - it 'is enabled by SiteSetting' do + describe "report-uri" do + it "is enabled by SiteSetting" do SiteSetting.content_security_policy_collect_reports = true - report_uri = parse(policy)['report-uri'].first - expect(report_uri).to eq('http://test.localhost/csp_reports') + report_uri = parse(policy)["report-uri"].first + expect(report_uri).to eq("http://test.localhost/csp_reports") SiteSetting.content_security_policy_collect_reports = false - report_uri = parse(policy)['report-uri'] + report_uri = parse(policy)["report-uri"] expect(report_uri).to eq(nil) end end - describe 'base-uri' do - it 'is set to self' do - base_uri = parse(policy)['base-uri'] + describe "base-uri" do + it "is set to self" do + base_uri = parse(policy)["base-uri"] expect(base_uri).to eq(["'self'"]) end end - describe 'object-src' do - it 'is set to none' do - object_srcs = parse(policy)['object-src'] + describe "object-src" do + it "is set to none" do + object_srcs = parse(policy)["object-src"] expect(object_srcs).to eq(["'none'"]) end end - describe 'upgrade-insecure-requests' do - it 'is not included when force_https is off' do + describe "upgrade-insecure-requests" do + it "is not included when force_https is off" do SiteSetting.force_https = false - expect(parse(policy)['upgrade-insecure-requests']).to eq(nil) + expect(parse(policy)["upgrade-insecure-requests"]).to eq(nil) end - it 'is included when force_https is on' do + it "is included when force_https is on" do SiteSetting.force_https = true - expect(parse(policy)['upgrade-insecure-requests']).to eq([]) + expect(parse(policy)["upgrade-insecure-requests"]).to eq([]) end end - describe 'worker-src' do - it 'has expected values' do - worker_srcs = parse(policy)['worker-src'] - expect(worker_srcs).to eq(%w[ - 'self' - http://test.localhost/assets/ - http://test.localhost/brotli_asset/ - http://test.localhost/javascripts/ - http://test.localhost/plugins/ - ]) + describe "worker-src" do + it "has expected values" do + worker_srcs = parse(policy)["worker-src"] + expect(worker_srcs).to eq( + %w[ + 'self' + http://test.localhost/assets/ + http://test.localhost/brotli_asset/ + http://test.localhost/javascripts/ + http://test.localhost/plugins/ + ], + ) end end - describe 'script-src' do - it 'always has self, logster, sidekiq, and assets' do - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include(*%w[ - http://test.localhost/logs/ - http://test.localhost/sidekiq/ - http://test.localhost/mini-profiler-resources/ - http://test.localhost/assets/ - http://test.localhost/brotli_asset/ - http://test.localhost/extra-locales/ - http://test.localhost/highlight-js/ - http://test.localhost/javascripts/ - http://test.localhost/plugins/ - http://test.localhost/theme-javascripts/ - http://test.localhost/svg-sprite/ - ]) + describe "script-src" do + it "always has self, logster, sidekiq, and assets" do + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include( + *%w[ + http://test.localhost/logs/ + http://test.localhost/sidekiq/ + http://test.localhost/mini-profiler-resources/ + http://test.localhost/assets/ + http://test.localhost/brotli_asset/ + http://test.localhost/extra-locales/ + http://test.localhost/highlight-js/ + http://test.localhost/javascripts/ + http://test.localhost/plugins/ + http://test.localhost/theme-javascripts/ + http://test.localhost/svg-sprite/ + ], + ) end it 'includes "report-sample" when report collection is enabled' do SiteSetting.content_security_policy_collect_reports = true - script_srcs = parse(policy)['script-src'] + script_srcs = parse(policy)["script-src"] expect(script_srcs).to include("'report-sample'") end - context 'for Google Analytics' do - before do - SiteSetting.ga_universal_tracking_code = 'UA-12345678-9' + context "for Google Analytics" do + before { SiteSetting.ga_universal_tracking_code = "UA-12345678-9" } + + it "allowlists Google Analytics v3 when integrated" do + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include("https://www.google-analytics.com/analytics.js") + expect(script_srcs).not_to include("https://www.googletagmanager.com/gtag/js") end - it 'allowlists Google Analytics v3 when integrated' do - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include('https://www.google-analytics.com/analytics.js') - expect(script_srcs).not_to include('https://www.googletagmanager.com/gtag/js') - end + it "allowlists Google Analytics v4 when integrated" do + SiteSetting.ga_version = "v4_gtag" - it 'allowlists Google Analytics v4 when integrated' do - SiteSetting.ga_version = 'v4_gtag' - - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include('https://www.google-analytics.com/analytics.js') - expect(script_srcs).to include('https://www.googletagmanager.com/gtag/js') + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include("https://www.google-analytics.com/analytics.js") + expect(script_srcs).to include("https://www.googletagmanager.com/gtag/js") end end - it 'allowlists Google Tag Manager when integrated' do - SiteSetting.gtm_container_id = 'GTM-ABCDEF' + it "allowlists Google Tag Manager when integrated" do + SiteSetting.gtm_container_id = "GTM-ABCDEF" - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include('https://www.googletagmanager.com/gtm.js') - expect(script_srcs.to_s).to include('nonce-') + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include("https://www.googletagmanager.com/gtm.js") + expect(script_srcs.to_s).to include("nonce-") end - it 'allowlists CDN assets when integrated' do - set_cdn_url('https://cdn.com') + it "allowlists CDN assets when integrated" do + set_cdn_url("https://cdn.com") - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include(*%w[ - https://cdn.com/assets/ - https://cdn.com/brotli_asset/ - https://cdn.com/highlight-js/ - https://cdn.com/javascripts/ - https://cdn.com/plugins/ - https://cdn.com/theme-javascripts/ - http://test.localhost/extra-locales/ - ]) + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include( + *%w[ + https://cdn.com/assets/ + https://cdn.com/brotli_asset/ + https://cdn.com/highlight-js/ + https://cdn.com/javascripts/ + https://cdn.com/plugins/ + https://cdn.com/theme-javascripts/ + http://test.localhost/extra-locales/ + ], + ) - global_setting(:s3_cdn_url, 'https://s3-cdn.com') + global_setting(:s3_cdn_url, "https://s3-cdn.com") - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include(*%w[ - https://s3-cdn.com/assets/ - https://s3-cdn.com/brotli_asset/ - https://cdn.com/highlight-js/ - https://cdn.com/javascripts/ - https://cdn.com/plugins/ - https://cdn.com/theme-javascripts/ - http://test.localhost/extra-locales/ - ]) + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include( + *%w[ + https://s3-cdn.com/assets/ + https://s3-cdn.com/brotli_asset/ + https://cdn.com/highlight-js/ + https://cdn.com/javascripts/ + https://cdn.com/plugins/ + https://cdn.com/theme-javascripts/ + http://test.localhost/extra-locales/ + ], + ) - global_setting(:s3_asset_cdn_url, 'https://s3-asset-cdn.com') + global_setting(:s3_asset_cdn_url, "https://s3-asset-cdn.com") - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include(*%w[ - https://s3-asset-cdn.com/assets/ - https://s3-asset-cdn.com/brotli_asset/ - https://cdn.com/highlight-js/ - https://cdn.com/javascripts/ - https://cdn.com/plugins/ - https://cdn.com/theme-javascripts/ - http://test.localhost/extra-locales/ - ]) + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include( + *%w[ + https://s3-asset-cdn.com/assets/ + https://s3-asset-cdn.com/brotli_asset/ + https://cdn.com/highlight-js/ + https://cdn.com/javascripts/ + https://cdn.com/plugins/ + https://cdn.com/theme-javascripts/ + http://test.localhost/extra-locales/ + ], + ) end - it 'adds subfolder to CDN assets' do - set_cdn_url('https://cdn.com') - set_subfolder('/forum') + it "adds subfolder to CDN assets" do + set_cdn_url("https://cdn.com") + set_subfolder("/forum") - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include(*%w[ - https://cdn.com/forum/assets/ - https://cdn.com/forum/brotli_asset/ - https://cdn.com/forum/highlight-js/ - https://cdn.com/forum/javascripts/ - https://cdn.com/forum/plugins/ - https://cdn.com/forum/theme-javascripts/ - http://test.localhost/forum/extra-locales/ - ]) + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include( + *%w[ + https://cdn.com/forum/assets/ + https://cdn.com/forum/brotli_asset/ + https://cdn.com/forum/highlight-js/ + https://cdn.com/forum/javascripts/ + https://cdn.com/forum/plugins/ + https://cdn.com/forum/theme-javascripts/ + http://test.localhost/forum/extra-locales/ + ], + ) - global_setting(:s3_cdn_url, 'https://s3-cdn.com') + global_setting(:s3_cdn_url, "https://s3-cdn.com") - script_srcs = parse(policy)['script-src'] - expect(script_srcs).to include(*%w[ - https://s3-cdn.com/assets/ - https://s3-cdn.com/brotli_asset/ - https://cdn.com/forum/highlight-js/ - https://cdn.com/forum/javascripts/ - https://cdn.com/forum/plugins/ - https://cdn.com/forum/theme-javascripts/ - http://test.localhost/forum/extra-locales/ - ]) + script_srcs = parse(policy)["script-src"] + expect(script_srcs).to include( + *%w[ + https://s3-cdn.com/assets/ + https://s3-cdn.com/brotli_asset/ + https://cdn.com/forum/highlight-js/ + https://cdn.com/forum/javascripts/ + https://cdn.com/forum/plugins/ + https://cdn.com/forum/theme-javascripts/ + http://test.localhost/forum/extra-locales/ + ], + ) end end - describe 'manifest-src' do - it 'is set to self' do - expect(parse(policy)['manifest-src']).to eq(["'self'"]) + describe "manifest-src" do + it "is set to self" do + expect(parse(policy)["manifest-src"]).to eq(["'self'"]) end end - describe 'frame-ancestors' do - context 'with content_security_policy_frame_ancestors enabled' do + describe "frame-ancestors" do + context "with content_security_policy_frame_ancestors enabled" do before do SiteSetting.content_security_policy_frame_ancestors = true - Fabricate(:embeddable_host, host: 'https://a.org') - Fabricate(:embeddable_host, host: 'https://b.org') + Fabricate(:embeddable_host, host: "https://a.org") + Fabricate(:embeddable_host, host: "https://b.org") end - it 'always has self' do - frame_ancestors = parse(policy)['frame-ancestors'] + it "always has self" do + frame_ancestors = parse(policy)["frame-ancestors"] expect(frame_ancestors).to include("'self'") end - it 'includes all EmbeddableHost' do + it "includes all EmbeddableHost" do EmbeddableHost - frame_ancestors = parse(policy)['frame-ancestors'] + frame_ancestors = parse(policy)["frame-ancestors"] expect(frame_ancestors).to include("https://a.org") expect(frame_ancestors).to include("https://b.org") end end - context 'with content_security_policy_frame_ancestors disabled' do - before do - SiteSetting.content_security_policy_frame_ancestors = false - end + context "with content_security_policy_frame_ancestors disabled" do + before { SiteSetting.content_security_policy_frame_ancestors = false } - it 'does not set frame-ancestors' do - frame_ancestors = parse(policy)['frame-ancestors'] + it "does not set frame-ancestors" do + frame_ancestors = parse(policy)["frame-ancestors"] expect(frame_ancestors).to be_nil end end end - context 'with a plugin' do + context "with a plugin" do let(:plugin_class) do Class.new(Plugin::Instance) do attr_accessor :enabled @@ -227,29 +235,29 @@ RSpec.describe ContentSecurityPolicy do end end - it 'can extend script-src, object-src, manifest-src' do + it "can extend script-src, object-src, manifest-src" do plugin = plugin_class.new(nil, "#{Rails.root}/spec/fixtures/plugins/csp_extension/plugin.rb") plugin.activate! Discourse.plugins << plugin plugin.enabled = true - expect(parse(policy)['script-src']).to include('https://from-plugin.com') - expect(parse(policy)['script-src']).to include('http://test.localhost/local/path') - expect(parse(policy)['object-src']).to include('https://test-stripping.com') - expect(parse(policy)['object-src']).to_not include("'none'") - expect(parse(policy)['manifest-src']).to include("'self'") - expect(parse(policy)['manifest-src']).to include('https://manifest-src.com') + expect(parse(policy)["script-src"]).to include("https://from-plugin.com") + expect(parse(policy)["script-src"]).to include("http://test.localhost/local/path") + expect(parse(policy)["object-src"]).to include("https://test-stripping.com") + expect(parse(policy)["object-src"]).to_not include("'none'") + expect(parse(policy)["manifest-src"]).to include("'self'") + expect(parse(policy)["manifest-src"]).to include("https://manifest-src.com") plugin.enabled = false - expect(parse(policy)['script-src']).to_not include('https://from-plugin.com') - expect(parse(policy)['manifest-src']).to_not include('https://manifest-src.com') + expect(parse(policy)["script-src"]).to_not include("https://from-plugin.com") + expect(parse(policy)["manifest-src"]).to_not include("https://manifest-src.com") Discourse.plugins.delete plugin DiscoursePluginRegistry.reset! end - it 'can extend frame_ancestors' do + it "can extend frame_ancestors" do SiteSetting.content_security_policy_frame_ancestors = true plugin = plugin_class.new(nil, "#{Rails.root}/spec/fixtures/plugins/csp_extension/plugin.rb") @@ -257,25 +265,25 @@ RSpec.describe ContentSecurityPolicy do Discourse.plugins << plugin plugin.enabled = true - expect(parse(policy)['frame-ancestors']).to include("'self'") - expect(parse(policy)['frame-ancestors']).to include('https://frame-ancestors-plugin.ext') + expect(parse(policy)["frame-ancestors"]).to include("'self'") + expect(parse(policy)["frame-ancestors"]).to include("https://frame-ancestors-plugin.ext") plugin.enabled = false - expect(parse(policy)['frame-ancestors']).to_not include('https://frame-ancestors-plugin.ext') + expect(parse(policy)["frame-ancestors"]).to_not include("https://frame-ancestors-plugin.ext") Discourse.plugins.delete plugin DiscoursePluginRegistry.reset! end end - it 'only includes unsafe-inline for qunit paths' do - expect(parse(policy(path_info: "/qunit"))['script-src']).to include("'unsafe-eval'") - expect(parse(policy(path_info: "/wizard/qunit"))['script-src']).to include("'unsafe-eval'") - expect(parse(policy(path_info: "/"))['script-src']).to_not include("'unsafe-eval'") + it "only includes unsafe-inline for qunit paths" do + expect(parse(policy(path_info: "/qunit"))["script-src"]).to include("'unsafe-eval'") + expect(parse(policy(path_info: "/wizard/qunit"))["script-src"]).to include("'unsafe-eval'") + expect(parse(policy(path_info: "/"))["script-src"]).to_not include("'unsafe-eval'") end context "with a theme" do - let!(:theme) { + let!(:theme) do Fabricate(:theme).tap do |t| settings = <<~YML extend_content_security_policy: @@ -285,58 +293,67 @@ RSpec.describe ContentSecurityPolicy do t.set_field(target: :settings, name: :yaml, value: settings) t.save! end - } + end def theme_policy policy(theme.id) end - it 'can be extended by themes' do + it "can be extended by themes" do policy # call this first to make sure further actions clear the cache - expect(parse(policy)['script-src']).not_to include('from-theme.com') + expect(parse(policy)["script-src"]).not_to include("from-theme.com") - expect(parse(theme_policy)['script-src']).to include('from-theme.com') + expect(parse(theme_policy)["script-src"]).to include("from-theme.com") - theme.update_setting(:extend_content_security_policy, "script-src: https://from-theme.net|worker-src: from-theme.com") + theme.update_setting( + :extend_content_security_policy, + "script-src: https://from-theme.net|worker-src: from-theme.com", + ) theme.save! - expect(parse(theme_policy)['script-src']).to_not include('from-theme.com') - expect(parse(theme_policy)['script-src']).to include('https://from-theme.net') - expect(parse(theme_policy)['worker-src']).to include('from-theme.com') + expect(parse(theme_policy)["script-src"]).to_not include("from-theme.com") + expect(parse(theme_policy)["script-src"]).to include("https://from-theme.net") + expect(parse(theme_policy)["worker-src"]).to include("from-theme.com") theme.destroy! - expect(parse(theme_policy)['script-src']).to_not include('https://from-theme.net') - expect(parse(theme_policy)['worker-src']).to_not include('from-theme.com') + expect(parse(theme_policy)["script-src"]).to_not include("https://from-theme.net") + expect(parse(theme_policy)["worker-src"]).to_not include("from-theme.com") end - it 'can be extended by theme modifiers' do + it "can be extended by theme modifiers" do policy # call this first to make sure further actions clear the cache - theme.theme_modifier_set.csp_extensions = ["script-src: https://from-theme-flag.script", "worker-src: from-theme-flag.worker"] + theme.theme_modifier_set.csp_extensions = [ + "script-src: https://from-theme-flag.script", + "worker-src: from-theme-flag.worker", + ] theme.save! child_theme = Fabricate(:theme, component: true) theme.add_relative_theme!(:child, child_theme) - child_theme.theme_modifier_set.csp_extensions = ["script-src: https://child-theme-flag.script", "worker-src: child-theme-flag.worker"] + child_theme.theme_modifier_set.csp_extensions = [ + "script-src: https://child-theme-flag.script", + "worker-src: child-theme-flag.worker", + ] child_theme.save! - expect(parse(theme_policy)['script-src']).to include('https://from-theme-flag.script') - expect(parse(theme_policy)['script-src']).to include('https://child-theme-flag.script') - expect(parse(theme_policy)['worker-src']).to include('from-theme-flag.worker') - expect(parse(theme_policy)['worker-src']).to include('child-theme-flag.worker') + expect(parse(theme_policy)["script-src"]).to include("https://from-theme-flag.script") + expect(parse(theme_policy)["script-src"]).to include("https://child-theme-flag.script") + expect(parse(theme_policy)["worker-src"]).to include("from-theme-flag.worker") + expect(parse(theme_policy)["worker-src"]).to include("child-theme-flag.worker") theme.destroy! child_theme.destroy! - expect(parse(theme_policy)['script-src']).to_not include('https://from-theme-flag.script') - expect(parse(theme_policy)['worker-src']).to_not include('from-theme-flag.worker') - expect(parse(theme_policy)['worker-src']).to_not include('from-theme-flag.worker') - expect(parse(theme_policy)['worker-src']).to_not include('child-theme-flag.worker') + expect(parse(theme_policy)["script-src"]).to_not include("https://from-theme-flag.script") + expect(parse(theme_policy)["worker-src"]).to_not include("from-theme-flag.worker") + expect(parse(theme_policy)["worker-src"]).to_not include("from-theme-flag.worker") + expect(parse(theme_policy)["worker-src"]).to_not include("child-theme-flag.worker") end - it 'is extended automatically when themes reference external scripts' do + it "is extended automatically when themes reference external scripts" do policy # call this first to make sure further actions clear the cache theme.set_field(target: :common, name: "header", value: <<~HTML) @@ -350,30 +367,35 @@ RSpec.describe ContentSecurityPolicy do theme.set_field(target: :desktop, name: "header", value: "") theme.save! - expect(parse(theme_policy)['script-src']).to include('https://example.com/myscript.js') - expect(parse(theme_policy)['script-src']).to include('https://example.com/myscript2.js') - expect(parse(theme_policy)['script-src']).not_to include('?') - expect(parse(theme_policy)['script-src']).to include('example2.com/protocol-less-script.js') - expect(parse(theme_policy)['script-src']).not_to include('domain-only.com') - expect(parse(theme_policy)['script-src']).not_to include(a_string_matching /^\/theme-javascripts/) + expect(parse(theme_policy)["script-src"]).to include("https://example.com/myscript.js") + expect(parse(theme_policy)["script-src"]).to include("https://example.com/myscript2.js") + expect(parse(theme_policy)["script-src"]).not_to include("?") + expect(parse(theme_policy)["script-src"]).to include("example2.com/protocol-less-script.js") + expect(parse(theme_policy)["script-src"]).not_to include("domain-only.com") + expect(parse(theme_policy)["script-src"]).not_to include( + a_string_matching %r{^/theme-javascripts} + ) theme.destroy! - expect(parse(theme_policy)['script-src']).to_not include('https://example.com/myscript.js') + expect(parse(theme_policy)["script-src"]).to_not include("https://example.com/myscript.js") end end - it 'can be extended by site setting' do - SiteSetting.content_security_policy_script_src = 'from-site-setting.com|from-site-setting.net' + it "can be extended by site setting" do + SiteSetting.content_security_policy_script_src = "from-site-setting.com|from-site-setting.net" - expect(parse(policy)['script-src']).to include('from-site-setting.com', 'from-site-setting.net') + expect(parse(policy)["script-src"]).to include("from-site-setting.com", "from-site-setting.net") end def parse(csp_string) - csp_string.split(';').map do |policy| - directive, *sources = policy.split - [directive, sources] - end.to_h + csp_string + .split(";") + .map do |policy| + directive, *sources = policy.split + [directive, sources] + end + .to_h end def policy(theme_id = nil, path_info: "/") diff --git a/spec/lib/cooked_post_processor_spec.rb b/spec/lib/cooked_post_processor_spec.rb index c703ea1909e..f55ef0e6895 100644 --- a/spec/lib/cooked_post_processor_spec.rb +++ b/spec/lib/cooked_post_processor_spec.rb @@ -5,15 +5,13 @@ require "file_store/s3_store" RSpec.describe CookedPostProcessor do fab!(:upload) { Fabricate(:upload) } - fab!(:large_image_upload) { Fabricate(:large_image_upload) } + fab!(:large_image_upload) { Fabricate(:large_image_upload) } let(:upload_path) { Discourse.store.upload_path } describe "#post_process" do - fab!(:post) do - Fabricate(:post, raw: <<~RAW) + fab!(:post) { Fabricate(:post, raw: <<~RAW) } RAW - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } let(:post_process) { sequence("post_process") } @@ -27,21 +25,16 @@ RSpec.describe CookedPostProcessor do expect(UploadReference.exists?(target: post, upload: upload)).to eq(true) end - describe 'when post contains oneboxes and inline oneboxes' do - let(:url_hostname) { 'meta.discourse.org' } + describe "when post contains oneboxes and inline oneboxes" do + let(:url_hostname) { "meta.discourse.org" } - let(:url) do - "https://#{url_hostname}/t/mini-inline-onebox-support-rfc/66400" - end + let(:url) { "https://#{url_hostname}/t/mini-inline-onebox-support-rfc/66400" } - let(:not_oneboxed_url) do - "https://#{url_hostname}/t/random-url" - end + let(:not_oneboxed_url) { "https://#{url_hostname}/t/random-url" } - let(:title) { 'some title' } + let(:title) { "some title" } - let(:post) do - Fabricate(:post, raw: <<~RAW) + let(:post) { Fabricate(:post, raw: <<~RAW) } #{url} This is a #{url} with path @@ -52,7 +45,6 @@ RSpec.describe CookedPostProcessor do #{url} RAW - end before do SiteSetting.enable_inline_onebox_on_all_domains = true @@ -71,10 +63,8 @@ RSpec.describe CookedPostProcessor do HTML Oneboxer.stubs(:cached_onebox).with(not_oneboxed_url).returns(nil) - %i{head get}.each do |method| - stub_request(method, url).to_return( - status: 200, - body: <<~RAW + %i[head get].each do |method| + stub_request(method, url).to_return(status: 200, body: <<~RAW) #{title} @@ -83,7 +73,6 @@ RSpec.describe CookedPostProcessor do RAW - ) end end @@ -92,47 +81,50 @@ RSpec.describe CookedPostProcessor do Oneboxer.invalidate(url) end - it 'should respect SiteSetting.max_oneboxes_per_post' do + it "should respect SiteSetting.max_oneboxes_per_post" do SiteSetting.max_oneboxes_per_post = 2 SiteSetting.add_rel_nofollow_to_user_content = false cpp.post_process - expect(cpp.html).to have_tag('a', - with: { href: url, class: "inline-onebox" }, + expect(cpp.html).to have_tag( + "a", + with: { + href: url, + class: "inline-onebox", + }, text: title, - count: 2 + count: 2, ) - expect(cpp.html).to have_tag('aside.onebox a', text: title, count: 1) + expect(cpp.html).to have_tag("aside.onebox a", text: title, count: 1) - expect(cpp.html).to have_tag('aside.onebox a', - text: url_hostname, - count: 1 - ) + expect(cpp.html).to have_tag("aside.onebox a", text: url_hostname, count: 1) - expect(cpp.html).to have_tag('a', - without: { class: "inline-onebox-loading" }, - text: not_oneboxed_url, - count: 1 - ) - - expect(cpp.html).to have_tag('a', + expect(cpp.html).to have_tag( + "a", without: { - class: 'onebox' + class: "inline-onebox-loading", }, text: not_oneboxed_url, - count: 1 + count: 1, + ) + + expect(cpp.html).to have_tag( + "a", + without: { + class: "onebox", + }, + text: not_oneboxed_url, + count: 1, ) end end - describe 'when post contains inline oneboxes' do - before do - SiteSetting.enable_inline_onebox_on_all_domains = true - end + describe "when post contains inline oneboxes" do + before { SiteSetting.enable_inline_onebox_on_all_domains = true } - describe 'internal links' do + describe "internal links" do fab!(:topic) { Fabricate(:topic) } fab!(:post) { Fabricate(:post, raw: "Hello #{topic.url}") } let(:url) { topic.url } @@ -140,52 +132,49 @@ RSpec.describe CookedPostProcessor do it "includes the topic title" do cpp.post_process - expect(cpp.html).to have_tag('a', - with: { href: UrlHelper.cook_url(url) }, - without: { class: "inline-onebox-loading" }, + expect(cpp.html).to have_tag( + "a", + with: { + href: UrlHelper.cook_url(url), + }, + without: { + class: "inline-onebox-loading", + }, text: topic.title, - count: 1 + count: 1, ) topic.update!(title: "Updated to something else") cpp = CookedPostProcessor.new(post, invalidate_oneboxes: true) cpp.post_process - expect(cpp.html).to have_tag('a', - with: { href: UrlHelper.cook_url(url) }, - without: { class: "inline-onebox-loading" }, + expect(cpp.html).to have_tag( + "a", + with: { + href: UrlHelper.cook_url(url), + }, + without: { + class: "inline-onebox-loading", + }, text: topic.title, - count: 1 + count: 1, ) end end - describe 'external links' do - let(:url_with_path) do - 'https://meta.discourse.org/t/mini-inline-onebox-support-rfc/66400' - end + describe "external links" do + let(:url_with_path) { "https://meta.discourse.org/t/mini-inline-onebox-support-rfc/66400" } - let(:url_with_query_param) do - 'https://meta.discourse.org?a' - end + let(:url_with_query_param) { "https://meta.discourse.org?a" } - let(:url_no_path) do - 'https://meta.discourse.org/' - end + let(:url_no_path) { "https://meta.discourse.org/" } - let(:urls) do - [ - url_with_path, - url_with_query_param, - url_no_path - ] - end + let(:urls) { [url_with_path, url_with_query_param, url_no_path] } - let(:title) { 'some title' } + let(:title) { "some title" } let(:escaped_title) { CGI.escapeHTML(title) } - let(:post) do - Fabricate(:post, raw: <<~RAW) + let(:post) { Fabricate(:post, raw: <<~RAW) } This is a #{url_with_path} topic This should not be inline #{url_no_path} oneboxed @@ -194,55 +183,65 @@ RSpec.describe CookedPostProcessor do - #{url_with_query_param} RAW - end - let(:staff_post) do - Fabricate(:post, user: Fabricate(:admin), raw: <<~RAW) + let(:staff_post) { Fabricate(:post, user: Fabricate(:admin), raw: <<~RAW) } This is a #{url_with_path} topic RAW - end before do urls.each do |url| stub_request(:get, url).to_return( status: 200, - body: "#{escaped_title}" + body: "#{escaped_title}", ) end end - after do - urls.each { |url| InlineOneboxer.invalidate(url) } - end + after { urls.each { |url| InlineOneboxer.invalidate(url) } } - it 'should convert the right links to inline oneboxes' do + it "should convert the right links to inline oneboxes" do cpp.post_process html = cpp.html - expect(html).to_not have_tag('a', - with: { href: url_no_path }, - without: { class: "inline-onebox-loading" }, - text: title + expect(html).to_not have_tag( + "a", + with: { + href: url_no_path, + }, + without: { + class: "inline-onebox-loading", + }, + text: title, ) - expect(html).to have_tag('a', - with: { href: url_with_path }, - without: { class: "inline-onebox-loading" }, + expect(html).to have_tag( + "a", + with: { + href: url_with_path, + }, + without: { + class: "inline-onebox-loading", + }, text: title, - count: 2 + count: 2, ) - expect(html).to have_tag('a', - with: { href: url_with_query_param }, - without: { class: "inline-onebox-loading" }, + expect(html).to have_tag( + "a", + with: { + href: url_with_query_param, + }, + without: { + class: "inline-onebox-loading", + }, text: title, - count: 1 + count: 1, ) expect(html).to have_tag("a[rel='noopener nofollow ugc']") end - it 'removes nofollow if user is staff/tl3' do + it "removes nofollow if user is staff/tl3" do cpp = CookedPostProcessor.new(staff_post, invalidate_oneboxes: true) cpp.post_process expect(cpp.html).to_not have_tag("a[rel='noopener nofollow ugc']") @@ -251,15 +250,13 @@ RSpec.describe CookedPostProcessor do end context "when processing images" do - before do - SiteSetting.responsive_post_image_sizes = "" - end + before { SiteSetting.responsive_post_image_sizes = "" } context "with responsive images" do before { SiteSetting.responsive_post_image_sizes = "1|1.5|3" } it "includes responsive images on demand" do - upload.update!(width: 2000, height: 1500, filesize: 10000, dominant_color: "FFFFFF") + upload.update!(width: 2000, height: 1500, filesize: 10_000, dominant_color: "FFFFFF") post = Fabricate(:post, raw: "hello ") # fake some optimized images @@ -269,9 +266,9 @@ RSpec.describe CookedPostProcessor do height: 500, upload_id: upload.id, sha1: SecureRandom.hex, - extension: '.jpg', + extension: ".jpg", filesize: 500, - version: OptimizedImage::VERSION + version: OptimizedImage::VERSION, ) # fake 3x optimized image, we lose 2 pixels here over original due to rounding on downsize @@ -281,8 +278,8 @@ RSpec.describe CookedPostProcessor do height: 1500, upload_id: upload.id, sha1: SecureRandom.hex, - extension: '.jpg', - filesize: 800 + extension: ".jpg", + filesize: 800, ) cpp = CookedPostProcessor.new(post) @@ -294,7 +291,9 @@ RSpec.describe CookedPostProcessor do expect(html).to include(%Q|data-dominant-color="FFFFFF"|) # 1.5x is skipped cause we have a missing thumb - expect(html).to include("srcset=\"//test.localhost/#{upload_path}/666x500.jpg, //test.localhost/#{upload_path}/1998x1500.jpg 3x\"") + expect(html).to include( + "srcset=\"//test.localhost/#{upload_path}/666x500.jpg, //test.localhost/#{upload_path}/1998x1500.jpg 3x\"", + ) expect(html).to include("src=\"//test.localhost/#{upload_path}/666x500.jpg\"") # works with CDN @@ -307,23 +306,25 @@ RSpec.describe CookedPostProcessor do html = cpp.html expect(html).to include(%Q|data-dominant-color="FFFFFF"|) - expect(html).to include("srcset=\"//cdn.localhost/#{upload_path}/666x500.jpg, //cdn.localhost/#{upload_path}/1998x1500.jpg 3x\"") + expect(html).to include( + "srcset=\"//cdn.localhost/#{upload_path}/666x500.jpg, //cdn.localhost/#{upload_path}/1998x1500.jpg 3x\"", + ) expect(html).to include("src=\"//cdn.localhost/#{upload_path}/666x500.jpg\"") end it "doesn't include response images for cropped images" do - upload.update!(width: 200, height: 4000, filesize: 12345) + upload.update!(width: 200, height: 4000, filesize: 12_345) post = Fabricate(:post, raw: "hello ") # fake some optimized images OptimizedImage.create!( - url: 'http://a.b.c/200x500.jpg', + url: "http://a.b.c/200x500.jpg", width: 200, height: 500, upload_id: upload.id, sha1: SecureRandom.hex, - extension: '.jpg', - filesize: 500 + extension: ".jpg", + filesize: 500, ) cpp = CookedPostProcessor.new(post) @@ -336,8 +337,8 @@ RSpec.describe CookedPostProcessor do shared_examples "leave dimensions alone" do it "doesn't use them" do - expect(cpp.html).to match(/src="http:\/\/foo.bar\/image.png" width="" height=""/) - expect(cpp.html).to match(/src="http:\/\/domain.com\/picture.jpg" width="50" height="42"/) + expect(cpp.html).to match(%r{src="http://foo.bar/image.png" width="" height=""}) + expect(cpp.html).to match(%r{src="http://domain.com/picture.jpg" width="50" height="42"}) expect(cpp).to be_dirty end end @@ -352,11 +353,15 @@ RSpec.describe CookedPostProcessor do end context "when valid" do - let(:image_sizes) { { "http://foo.bar/image.png" => { "width" => 111, "height" => 222 } } } + let(:image_sizes) do + { "http://foo.bar/image.png" => { "width" => 111, "height" => 222 } } + end it "uses them" do - expect(cpp.html).to match(/src="http:\/\/foo.bar\/image.png" width="111" height="222"/) - expect(cpp.html).to match(/src="http:\/\/domain.com\/picture.jpg" width="50" height="42"/) + expect(cpp.html).to match(%r{src="http://foo.bar/image.png" width="111" height="222"}) + expect(cpp.html).to match( + %r{src="http://domain.com/picture.jpg" width="50" height="42"}, + ) expect(cpp).to be_dirty end end @@ -375,17 +380,14 @@ RSpec.describe CookedPostProcessor do let(:image_sizes) { { "http://foo.bar/image.png" => { "width" => 0, "height" => 0 } } } include_examples "leave dimensions alone" end - end context "with unsized images" do fab!(:upload) { Fabricate(:image_upload, width: 123, height: 456) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } HTML - end let(:cpp) { CookedPostProcessor.new(post) } @@ -394,17 +396,14 @@ RSpec.describe CookedPostProcessor do expect(cpp.html).to match(/width="123" height="456"/) expect(cpp).to be_dirty end - end context "with large images" do fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } @@ -423,15 +422,20 @@ RSpec.describe CookedPostProcessor do expect(cpp).to be_dirty end - context 'when image is inside onebox' do - let(:url) { 'https://image.com/my-avatar' } + context "when image is inside onebox" do + let(:url) { "https://image.com/my-avatar" } let(:post) { Fabricate(:post, raw: url) } before do - Oneboxer.stubs(:onebox).with(url, anything).returns("") + Oneboxer + .stubs(:onebox) + .with(url, anything) + .returns( + "", + ) end - it 'should not add lightbox' do + it "should not add lightbox" do FastImage.expects(:size).returns([1750, 2000]) cpp.post_process @@ -442,12 +446,15 @@ RSpec.describe CookedPostProcessor do end end - context 'when image is an svg' do + context "when image is an svg" do fab!(:post) do - Fabricate(:post, raw: "") + Fabricate( + :post, + raw: "", + ) end - it 'should not add lightbox' do + it "should not add lightbox" do FastImage.expects(:size).returns([1750, 2000]) cpp.post_process @@ -457,17 +464,23 @@ RSpec.describe CookedPostProcessor do HTML end - context 'when image src is an URL' do + context "when image src is an URL" do let(:post) do - Fabricate(:post, raw: "") + Fabricate( + :post, + raw: + "", + ) end - it 'should not add lightbox' do + it "should not add lightbox" do FastImage.expects(:size).returns([1750, 2000]) cpp.post_process - expect(cpp.html).to match_html("

") + expect(cpp.html).to match_html( + "

", + ) end end end @@ -495,19 +508,23 @@ RSpec.describe CookedPostProcessor do Fabricate(:post, raw: "![large.png|#{optimized_size}](#{upload.short_url})") end - let(:cooked_html) do - <<~HTML + let(:cooked_html) { <<~HTML }

HTML - end context "when the upload is attached to the correct post" do before do FastImage.expects(:size).returns([1750, 2000]) OptimizedImage.expects(:resize).returns(true) - Discourse.store.class.any_instance.expects(:has_been_uploaded?).at_least_once.returns(true) + Discourse + .store + .class + .any_instance + .expects(:has_been_uploaded?) + .at_least_once + .returns(true) upload.update(secure: true, access_control_post: post) end @@ -540,17 +557,13 @@ RSpec.describe CookedPostProcessor do context "with tall images > default aspect ratio" do fab!(:upload) { Fabricate(:image_upload, width: 500, height: 2200) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } - before do - SiteSetting.create_thumbnails = true - end + before { SiteSetting.create_thumbnails = true } it "resizes the image instead of crop" do cpp.post_process @@ -558,23 +571,18 @@ RSpec.describe CookedPostProcessor do expect(cpp.html).to match(/width="113" height="500">/) expect(cpp).to be_dirty end - end context "with taller images < default aspect ratio" do fab!(:upload) { Fabricate(:image_upload, width: 500, height: 2300) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } - before do - SiteSetting.create_thumbnails = true - end + before { SiteSetting.create_thumbnails = true } it "crops the image" do cpp.post_process @@ -582,23 +590,18 @@ RSpec.describe CookedPostProcessor do expect(cpp.html).to match(/width="500" height="500">/) expect(cpp).to be_dirty end - end context "with iPhone X screenshots" do fab!(:upload) { Fabricate(:image_upload, width: 1125, height: 2436) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } - before do - SiteSetting.create_thumbnails = true - end + before { SiteSetting.create_thumbnails = true } it "crops the image" do cpp.post_process @@ -609,23 +612,23 @@ RSpec.describe CookedPostProcessor do expect(cpp).to be_dirty end - end context "with large images when using subfolders" do fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } before do set_subfolder "/subfolder" - stub_request(:get, "http://#{Discourse.current_hostname}/subfolder#{upload.url}").to_return(status: 200, body: File.new(Discourse.store.path_for(upload))) + stub_request( + :get, + "http://#{Discourse.current_hostname}/subfolder#{upload.url}", + ).to_return(status: 200, body: File.new(Discourse.store.path_for(upload))) SiteSetting.max_image_height = 2000 SiteSetting.create_thumbnails = true @@ -634,7 +637,7 @@ RSpec.describe CookedPostProcessor do it "generates overlay information" do cpp.post_process - expect(cpp.html). to match_html <<~HTML + expect(cpp.html).to match_html <<~HTML

HTML @@ -649,17 +652,14 @@ RSpec.describe CookedPostProcessor do

HTML end - end context "with title and alt" do fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } RED HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } @@ -677,17 +677,14 @@ RSpec.describe CookedPostProcessor do expect(cpp).to be_dirty end - end context "with title only" do fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } @@ -705,17 +702,14 @@ RSpec.describe CookedPostProcessor do expect(cpp).to be_dirty end - end context "with alt only" do fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) } - fab!(:post) do - Fabricate(:post, raw: <<~HTML) + fab!(:post) { Fabricate(:post, raw: <<~HTML) } RED HTML - end let(:cpp) { CookedPostProcessor.new(post, disable_dominant_color: true) } @@ -733,7 +727,6 @@ RSpec.describe CookedPostProcessor do expect(cpp).to be_dirty end - end context "with topic image" do @@ -839,29 +832,29 @@ RSpec.describe CookedPostProcessor do let(:cpp) { CookedPostProcessor.new(post) } it "returns the size when width and height are specified" do - img = { 'src' => 'http://foo.bar/image3.png', 'width' => 50, 'height' => 70 } + img = { "src" => "http://foo.bar/image3.png", "width" => 50, "height" => 70 } expect(cpp.get_size_from_attributes(img)).to eq([50, 70]) end it "returns the size when width and height are floats" do - img = { 'src' => 'http://foo.bar/image3.png', 'width' => 50.2, 'height' => 70.1 } + img = { "src" => "http://foo.bar/image3.png", "width" => 50.2, "height" => 70.1 } expect(cpp.get_size_from_attributes(img)).to eq([50, 70]) end it "resizes when only width is specified" do - img = { 'src' => 'http://foo.bar/image3.png', 'width' => 100 } + img = { "src" => "http://foo.bar/image3.png", "width" => 100 } FastImage.expects(:size).returns([200, 400]) expect(cpp.get_size_from_attributes(img)).to eq([100, 200]) end it "resizes when only height is specified" do - img = { 'src' => 'http://foo.bar/image3.png', 'height' => 100 } + img = { "src" => "http://foo.bar/image3.png", "height" => 100 } FastImage.expects(:size).returns([100, 300]) expect(cpp.get_size_from_attributes(img)).to eq([33, 100]) end it "doesn't raise an error with a weird url" do - img = { 'src' => nil, 'height' => 100 } + img = { "src" => nil, "height" => 100 } expect(cpp.get_size_from_attributes(img)).to be_nil end end @@ -940,7 +933,9 @@ RSpec.describe CookedPostProcessor do it "returns a generic name for pasted images" do upload = build(:upload, original_filename: "blob.png") - expect(cpp.get_filename(upload, "http://domain.com/image.png")).to eq(I18n.t('upload.pasted_image_filename')) + expect(cpp.get_filename(upload, "http://domain.com/image.png")).to eq( + I18n.t("upload.pasted_image_filename"), + ) end end @@ -952,10 +947,10 @@ RSpec.describe CookedPostProcessor do cpp = CookedPostProcessor.new(post, disable_dominant_color: true) cpp.post_process - doc = Nokogiri::HTML5::fragment(cpp.html) + doc = Nokogiri::HTML5.fragment(cpp.html) - expect(doc.css('.lightbox-wrapper').size).to eq(1) - expect(doc.css('img').first['srcset']).to_not eq(nil) + expect(doc.css(".lightbox-wrapper").size).to eq(1) + expect(doc.css("img").first["srcset"]).to_not eq(nil) end it "processes animated images correctly" do @@ -968,35 +963,52 @@ RSpec.describe CookedPostProcessor do cpp = CookedPostProcessor.new(post, disable_dominant_color: true) cpp.post_process - doc = Nokogiri::HTML5::fragment(cpp.html) - expect(doc.css('.lightbox-wrapper').size).to eq(0) - expect(doc.css('img').first['src']).to include(upload.url) - expect(doc.css('img').first['srcset']).to eq(nil) - expect(doc.css('img.animated').size).to eq(1) + doc = Nokogiri::HTML5.fragment(cpp.html) + expect(doc.css(".lightbox-wrapper").size).to eq(0) + expect(doc.css("img").first["src"]).to include(upload.url) + expect(doc.css("img").first["srcset"]).to eq(nil) + expect(doc.css("img.animated").size).to eq(1) end context "with giphy/tenor images" do before do - CookedPostProcessor.any_instance.stubs(:get_size).with("https://media2.giphy.com/media/7Oifk90VrCdNe/giphy.webp").returns([311, 280]) - CookedPostProcessor.any_instance.stubs(:get_size).with("https://media1.tenor.com/images/20c7ddd5e84c7427954f430439c5209d/tenor.gif").returns([833, 104]) + CookedPostProcessor + .any_instance + .stubs(:get_size) + .with("https://media2.giphy.com/media/7Oifk90VrCdNe/giphy.webp") + .returns([311, 280]) + CookedPostProcessor + .any_instance + .stubs(:get_size) + .with("https://media1.tenor.com/images/20c7ddd5e84c7427954f430439c5209d/tenor.gif") + .returns([833, 104]) end it "marks giphy images as animated" do - post = Fabricate(:post, raw: "![tennis-gif|311x280](https://media2.giphy.com/media/7Oifk90VrCdNe/giphy.webp)") + post = + Fabricate( + :post, + raw: "![tennis-gif|311x280](https://media2.giphy.com/media/7Oifk90VrCdNe/giphy.webp)", + ) cpp = CookedPostProcessor.new(post, disable_dominant_color: true) cpp.post_process - doc = Nokogiri::HTML5::fragment(cpp.html) - expect(doc.css('img.animated').size).to eq(1) + doc = Nokogiri::HTML5.fragment(cpp.html) + expect(doc.css("img.animated").size).to eq(1) end it "marks giphy images as animated" do - post = Fabricate(:post, raw: "![cat](https://media1.tenor.com/images/20c7ddd5e84c7427954f430439c5209d/tenor.gif)") + post = + Fabricate( + :post, + raw: + "![cat](https://media1.tenor.com/images/20c7ddd5e84c7427954f430439c5209d/tenor.gif)", + ) cpp = CookedPostProcessor.new(post, disable_dominant_color: true) cpp.post_process - doc = Nokogiri::HTML5::fragment(cpp.html) - expect(doc.css('img.animated').size).to eq(1) + doc = Nokogiri::HTML5.fragment(cpp.html) + expect(doc.css("img.animated").size).to eq(1) end end @@ -1010,24 +1022,27 @@ RSpec.describe CookedPostProcessor do cpp = CookedPostProcessor.new(post, disable_dominant_color: true) cpp.post_process - doc = Nokogiri::HTML5::fragment(cpp.html) - expect(doc.css('.lightbox-wrapper').size).to eq(0) - expect(doc.css('img').first['srcset']).to_not eq(nil) + doc = Nokogiri::HTML5.fragment(cpp.html) + expect(doc.css(".lightbox-wrapper").size).to eq(0) + expect(doc.css("img").first["srcset"]).to_not eq(nil) end it "optimizes images in Onebox" do - Oneboxer.expects(:onebox) + Oneboxer + .expects(:onebox) .with("https://discourse.org", anything) - .returns("") + .returns( + "", + ) post = Fabricate(:post, raw: "https://discourse.org") cpp = CookedPostProcessor.new(post, disable_dominant_color: true) cpp.post_process - doc = Nokogiri::HTML5::fragment(cpp.html) - expect(doc.css('.lightbox-wrapper').size).to eq(0) - expect(doc.css('img').first['srcset']).to_not eq(nil) + doc = Nokogiri::HTML5.fragment(cpp.html) + expect(doc.css(".lightbox-wrapper").size).to eq(0) + expect(doc.css("img").first["srcset"]).to_not eq(nil) end end @@ -1038,7 +1053,12 @@ RSpec.describe CookedPostProcessor do before do Oneboxer .expects(:onebox) - .with("http://www.youtube.com/watch?v=9bZkp7q19f0", invalidate_oneboxes: true, user_id: nil, category_id: post.topic.category_id) + .with( + "http://www.youtube.com/watch?v=9bZkp7q19f0", + invalidate_oneboxes: true, + user_id: nil, + category_id: post.topic.category_id, + ) .returns("
GANGNAM STYLE
") cpp.post_process_oneboxes @@ -1050,29 +1070,41 @@ RSpec.describe CookedPostProcessor do end describe "replacing downloaded onebox image" do - let(:url) { 'https://image.com/my-avatar' } - let(:image_url) { 'https://image.com/avatar.png' } + let(:url) { "https://image.com/my-avatar" } + let(:image_url) { "https://image.com/avatar.png" } it "successfully replaces the image" do - Oneboxer.stubs(:onebox).with(url, anything).returns("") + Oneboxer + .stubs(:onebox) + .with(url, anything) + .returns("") post = Fabricate(:post, raw: url) upload.update!(url: "https://test.s3.amazonaws.com/something.png") - PostHotlinkedMedia.create!(url: "//image.com/avatar.png", post: post, status: 'downloaded', upload: upload) + PostHotlinkedMedia.create!( + url: "//image.com/avatar.png", + post: post, + status: "downloaded", + upload: upload, + ) cpp = CookedPostProcessor.new(post, invalidate_oneboxes: true) stub_image_size(width: 100, height: 200) cpp.post_process_oneboxes - expect(cpp.doc.to_s).to eq("

") + expect(cpp.doc.to_s).to eq( + "

", + ) upload.destroy! cpp = CookedPostProcessor.new(post, invalidate_oneboxes: true) stub_image_size(width: 100, height: 200) cpp.post_process_oneboxes - expect(cpp.doc.to_s).to eq("

") + expect(cpp.doc.to_s).to eq( + "

", + ) Oneboxer.unstub(:onebox) end @@ -1086,12 +1118,20 @@ RSpec.describe CookedPostProcessor do end it "does not use the direct URL, uses the cooked URL instead (because of the private ACL preventing w/h fetch)" do - Oneboxer.stubs(:onebox).with(url, anything).returns("") + Oneboxer + .stubs(:onebox) + .with(url, anything) + .returns("") post = Fabricate(:post, raw: url) upload.update!(url: "https://test.s3.amazonaws.com/something.png") - PostHotlinkedMedia.create!(url: "//image.com/avatar.png", post: post, status: 'downloaded', upload: upload) + PostHotlinkedMedia.create!( + url: "//image.com/avatar.png", + post: post, + status: "downloaded", + upload: upload, + ) cooked_url = "https://localhost/secure-uploads/test.png" UrlHelper.expects(:cook_url).with(upload.url, secure: true).returns(cooked_url) @@ -1100,31 +1140,38 @@ RSpec.describe CookedPostProcessor do stub_image_size(width: 100, height: 200) cpp.post_process_oneboxes - expect(cpp.doc.to_s).to eq("

") + expect(cpp.doc.to_s).to eq( + "

", + ) end end end it "replaces large image placeholder" do SiteSetting.max_image_size_kb = 4096 - url = 'https://image.com/my-avatar' - image_url = 'https://image.com/avatar.png' + url = "https://image.com/my-avatar" + image_url = "https://image.com/avatar.png" - Oneboxer.stubs(:onebox).with(url, anything).returns("") + Oneboxer + .stubs(:onebox) + .with(url, anything) + .returns("") post = Fabricate(:post, raw: url) - PostHotlinkedMedia.create!(url: "//image.com/avatar.png", post: post, status: 'too_large') + PostHotlinkedMedia.create!(url: "//image.com/avatar.png", post: post, status: "too_large") cpp = CookedPostProcessor.new(post, invalidate_oneboxes: true) cpp.post_process expect(cpp.doc.to_s).to match(/
/) - expect(cpp.doc.to_s).to include(I18n.t("upload.placeholders.too_large_humanized", max_size: "4 MB")) + expect(cpp.doc.to_s).to include( + I18n.t("upload.placeholders.too_large_humanized", max_size: "4 MB"), + ) end it "removes large images from onebox" do - url = 'https://example.com/article' + url = "https://example.com/article" Oneboxer.stubs(:onebox).with(url, anything).returns <<~HTML
") + Oneboxer + .expects(:onebox) + .with( + "http://www.youtube.com/watch?v=9bZkp7q19f0", + invalidate_oneboxes: true, + user_id: nil, + category_id: post.topic.category_id, + ) + .returns( + "
", + ) cpp.post_process_oneboxes - expect(cpp.html).to match_html('') + expect(cpp.html).to match_html( + '', + ) end it "applies aspect ratio when wrapped in link" do - Oneboxer.expects(:onebox) - .with("http://www.youtube.com/watch?v=9bZkp7q19f0", invalidate_oneboxes: true, user_id: nil, category_id: post.topic.category_id) - .returns("