diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..82322d14d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: Release + +on: + workflow_dispatch: # allow repo collaborators to publish gem + +permissions: + contents: write # required for `rake release` to push the release tag + id-token: write # required for workflow to publish gem + +jobs: + release: + if: github.repository == 'rpush/rpush' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # required to run `rake release`. + - name: Setup Ruby and install dependencies + uses: ruby/setup-ruby@v1 + with: + # setup-ruby implicitly uses .ruby-version + bundler-cache: true + + # Run `rake release` to create git tag and push to repository based on `lib/rpush/version.rb`. + # Then publish the new gem via trusted publishing + # Read more on https://guides.rubygems.org/trusted-publishing/releasing-gems/ + - name: Publish gem + uses: rubygems/release-gem@v1 + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="v$(bundle exec ruby -e 'puts Rpush::VERSION.to_s.chomp')" + + gh release create \ + $VERSION \ + --verify-tag \ + --generate-notes \ + --latest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0399e026..938484b3e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ env: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest services: postgres: @@ -52,21 +52,21 @@ jobs: strategy: fail-fast: false matrix: - gemfile: ['rails_6.0', 'rails_6.1', 'rails_7.0'] + gemfile: ['rails_6.0', 'rails_6.1', 'rails_7.0', 'rails_7.1', 'rails_7.2'] - ruby: ['2.7', '3.0', '3.1'] + ruby: ['3.0', '3.1', '3.2', '3.3'] client: ['active_record', 'redis'] exclude: - - ruby: '2.7' - gemfile: 'rails_7.0' + - ruby: '3.0' + gemfile: 'rails_7.2' env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 diff --git a/.rubocop.yml b/.rubocop.yml index 4d2ba87c9..ad6aa7e09 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,13 +1,17 @@ inherit_from: .rubocop_todo.yml -require: rubocop-performance +require: + - rubocop-performance + - rubocop-rake + - rubocop-rspec + - rubocop-rails AllCops: Exclude: - lib/generators/**/* - vendor/bundle/**/* NewCops: enable - TargetRubyVersion: 2.4 + TargetRubyVersion: 3.0 Layout/LineLength: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e55d8733a..e2c7c05ec 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,13 +1,37 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2022-01-21 11:42:55 UTC using RuboCop version 1.12.1. +# on 2024-09-06 21:28:45 UTC using RuboCop version 1.66.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 10 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Include. +# Include: **/*.gemspec +Gemspec/AddRuntimeDependency: + Exclude: + - 'rpush.gemspec' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Severity, Include. +# Include: **/*.gemspec +Gemspec/DeprecatedAttributeAssignment: + Exclude: + - 'rpush.gemspec' + +# Offense count: 20 +# Configuration parameters: EnforcedStyle, AllowedGems, Include. +# SupportedStyles: Gemfile, gems.rb, gemspec +# Include: **/*.gemspec, **/Gemfile, **/gems.rb +Gemspec/DevelopmentDependencies: + Exclude: + - 'rpush.gemspec' + +# Offense count: 12 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. # Include: **/*.gemspec Gemspec/OrderedDependencies: @@ -15,7 +39,7 @@ Gemspec/OrderedDependencies: - 'rpush.gemspec' # Offense count: 8 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: @@ -24,40 +48,42 @@ Layout/ArgumentAlignment: - 'lib/rpush/daemon/apnsp8/delivery.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentOneStep, IndentationWidth. # SupportedStyles: case, end Layout/CaseIndentation: Exclude: - - 'lib/rpush/client/active_model/gcm/notification.rb' + - 'lib/rpush/client/active_model/fcm/notification.rb' -# Offense count: 9 -# Cop supports --auto-correct. +# Offense count: 23 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: leading, trailing Layout/DotPosition: Exclude: - 'spec/functional/apns2_spec.rb' + - 'spec/functional/cli_spec.rb' + - 'spec/functional/embed_spec.rb' -# Offense count: 40 -# Cop supports --auto-correct. +# Offense count: 33 +# This cop supports safe autocorrection (--autocorrect). Layout/EmptyLineAfterGuardClause: Enabled: false # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/EmptyLineAfterMagicComment: Exclude: - 'rpush.gemspec' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: - 'spec/unit/client/shared/webpush/notification.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: around, only_before Layout/EmptyLinesAroundAccessModifier: @@ -66,23 +92,24 @@ Layout/EmptyLinesAroundAccessModifier: - 'lib/rpush/daemon/apnsp8/delivery.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowAliasSyntax, AllowedMethods. # AllowedMethods: alias_method, public, protected, private Layout/EmptyLinesAroundAttributeAccessor: Exclude: - 'lib/rpush/daemon/app_runner.rb' -# Offense count: 1 -# Cop supports --auto-correct. +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, no_empty_lines Layout/EmptyLinesAroundBlockBody: Exclude: + - 'spec/unit/client/active_record/fcm/notification_spec.rb' - 'spec/unit/client/shared/webpush/notification.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only Layout/EmptyLinesAroundClassBody: @@ -90,7 +117,7 @@ Layout/EmptyLinesAroundClassBody: - 'lib/rpush/daemon/webpush/delivery.rb' # Offense count: 5 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines Layout/EmptyLinesAroundModuleBody: @@ -100,14 +127,14 @@ Layout/EmptyLinesAroundModuleBody: - 'lib/rpush/daemon/webpush/delivery.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Exclude: - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_brackets Layout/FirstArrayElementIndentation: @@ -115,16 +142,8 @@ Layout/FirstArrayElementIndentation: - 'lib/rpush/daemon/store/active_record/reconnectable.rb' - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_braces -Layout/FirstHashElementIndentation: - Exclude: - - 'lib/rpush/client/active_model/gcm/notification.rb' - -# Offense count: 84 -# Cop supports --auto-correct. +# Offense count: 92 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table # SupportedColonStyles: key, separator, table @@ -136,57 +155,70 @@ Layout/HashAlignment: - 'lib/rpush/daemon/apns2/delivery.rb' - 'lib/rpush/daemon/apnsp8/delivery.rb' - 'lib/rpush/daemon/constants.rb' - - 'lib/rpush/daemon/gcm/delivery.rb' + - 'lib/rpush/daemon/fcm/delivery.rb' - 'lib/rpush/daemon/service_config_methods.rb' - 'lib/rpush/daemon/wns/delivery.rb' - 'lib/rpush/daemon/wpns/delivery.rb' - 'spec/functional/apns2_spec.rb' + - 'spec/functional/cli_spec.rb' + - 'spec/functional/embed_spec.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/HeredocIndentation: Exclude: - 'lib/rpush/daemon.rb' - 'lib/tasks/test.rake' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: normal, indented_internal_methods -Layout/IndentationConsistency: - Exclude: - - 'spec/functional/apns2_spec.rb' - # Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: Width, IgnoredPatterns. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Width, AllowedPatterns. Layout/IndentationWidth: Exclude: - 'examples/rpush.god' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/InitialIndentation: Exclude: - 'spec/unit/reflection_collection_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/LeadingEmptyLines: Exclude: - 'lib/rpush/client/redis.rb' -# Offense count: 6 -# Cop supports --auto-correct. +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, no_space +Layout/LineContinuationSpacing: + Exclude: + - 'lib/rpush/daemon/apns2/delivery.rb' + - 'lib/rpush/daemon/apnsp8/delivery.rb' + - 'spec/functional/apns2_spec.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: aligned, indented +Layout/LineEndStringConcatenationIndentation: + Exclude: + - 'lib/rpush/daemon/apns2/delivery.rb' + - 'lib/rpush/daemon/apnsp8/delivery.rb' + - 'spec/unit/daemon/pushy/delivery_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: symmetrical, new_line, same_line Layout/MultilineHashBraceLayout: Exclude: - 'spec/functional/apns2_spec.rb' - - 'spec/unit/daemon/gcm/delivery_spec.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: symmetrical, new_line, same_line Layout/MultilineMethodCallBraceLayout: @@ -194,35 +226,44 @@ Layout/MultilineMethodCallBraceLayout: - 'lib/rpush/daemon/apns2/delivery.rb' - 'lib/rpush/daemon/apnsp8/delivery.rb' -# Offense count: 4 -# Cop supports --auto-correct. +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented, indented_relative_to_receiver Layout/MultilineMethodCallIndentation: Exclude: - 'lib/rpush/daemon/wns/toast_request.rb' - 'lib/rpush/daemon/wpns/delivery.rb' - - 'spec/functional/apns2_spec.rb' # Offense count: 3 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented Layout/MultilineOperationIndentation: Exclude: - 'lib/rpush/client/active_model/webpush/notification.rb' -# Offense count: 6 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator. +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/SpaceAfterColon: + Exclude: + - 'spec/unit/daemon/fcm/delivery_spec.rb' + +# Offense count: 12 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals. # SupportedStylesForExponentOperator: space, no_space +# SupportedStylesForRationalLiterals: space, no_space Layout/SpaceAroundOperators: Exclude: - 'lib/rpush/client/active_model/webpush/notification.rb' - 'spec/functional/apns2_spec.rb' + - 'spec/functional/cli_spec.rb' + - 'spec/functional/embed_spec.rb' + - 'spec/unit/client/shared/fcm/notification.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space # SupportedStylesForEmptyBraces: space, no_space @@ -231,7 +272,7 @@ Layout/SpaceBeforeBlockBraces: - 'spec/unit/client/shared/webpush/notification.rb' # Offense count: 10 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBrackets: space, no_space @@ -242,34 +283,49 @@ Layout/SpaceInsideArrayLiteralBrackets: - 'lib/rpush/daemon/webpush/delivery.rb' - 'spec/unit/client/shared/webpush/app.rb' -# Offense count: 21 -# Cop supports --auto-correct. +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Exclude: + - 'spec/functional/fcm_spec.rb' + - 'spec/functional/retry_spec.rb' + +# Offense count: 35 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Exclude: + - 'spec/functional/fcm_spec.rb' + - 'spec/functional/retry_spec.rb' - 'spec/functional/webpush_spec.rb' + - 'spec/unit/client/shared/fcm/notification.rb' - 'spec/unit/client/shared/webpush/notification.rb' + - 'spec/unit/daemon/service_config_methods_spec.rb' - 'spec/unit/daemon/webpush/delivery_spec.rb' + - 'spec/unit/daemon/wns/delivery_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. -# SupportedStyles: space, no_space +# SupportedStyles: space, compact, no_space Layout/SpaceInsideParens: Exclude: - 'spec/unit/client/shared/webpush/notification.rb' # Offense count: 4 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/SpaceInsidePercentLiteralDelimiters: Exclude: - 'lib/rpush/client/active_model/webpush/app.rb' - 'lib/rpush/client/active_model/webpush/notification.rb' # Offense count: 4 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: final_newline, final_blank_line Layout/TrailingEmptyLines: @@ -279,7 +335,16 @@ Layout/TrailingEmptyLines: - 'lib/rpush/daemon/webpush/delivery.rb' - 'spec/functional/webpush_spec.rb' +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +Lint/AmbiguousOperatorPrecedence: + Exclude: + - 'lib/rpush/daemon/delivery.rb' + - 'spec/unit/daemon/adm/delivery_spec.rb' + - 'spec/unit/daemon/fcm/delivery_spec.rb' + # Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Exclude: @@ -297,10 +362,11 @@ Lint/ConstantDefinitionInBlock: - 'spec/unit/deprecatable_spec.rb' - 'spec/unit/reflectable_spec.rb' -# Offense count: 3 +# Offense count: 4 # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches. Lint/DuplicateBranch: Exclude: + - 'lib/rpush/client/active_model/fcm/notification.rb' - 'lib/rpush/daemon/wns/delivery.rb' - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' @@ -310,7 +376,7 @@ Lint/EmptyBlock: Exclude: - 'bm/reflection_benchmark.rb' - 'spec/unit/client/redis/apns2/app_spec.rb' - - 'spec/unit/client/shared/gcm/app.rb' + - 'spec/unit/client/shared/fcm/app.rb' - 'spec/unit/client/shared/wpns/app.rb' # Offense count: 2 @@ -320,109 +386,121 @@ Lint/EmptyClass: - 'spec/unit/daemon/app_runner_spec.rb' - 'spec/unit/daemon/service_config_methods_spec.rb' +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Lint/IncompatibleIoSelectWithFiberScheduler: + Exclude: + - 'lib/rpush/daemon/signal_handler.rb' + # Offense count: 6 Lint/IneffectiveAccessModifier: Exclude: - 'lib/rpush/daemon.rb' -# Offense count: 14 +# Offense count: 12 +# Configuration parameters: AllowedParentClasses. Lint/MissingSuper: Exclude: - 'lib/rpush/daemon/adm/delivery.rb' - - 'lib/rpush/daemon/apns/delivery.rb' - 'lib/rpush/daemon/apns2/delivery.rb' - 'lib/rpush/daemon/apnsp8/delivery.rb' - 'lib/rpush/daemon/delivery_error.rb' - 'lib/rpush/daemon/errors.rb' - - 'lib/rpush/daemon/gcm/delivery.rb' + - 'lib/rpush/daemon/fcm/delivery.rb' - 'lib/rpush/daemon/pushy/delivery.rb' - 'lib/rpush/daemon/retryable_error.rb' - 'lib/rpush/daemon/ring_buffer.rb' - 'lib/rpush/daemon/webpush/delivery.rb' - 'lib/rpush/daemon/wns/delivery.rb' - 'lib/rpush/daemon/wpns/delivery.rb' - - 'spec/unit/daemon/delivery_spec.rb' -# Offense count: 1 -Lint/NestedMethodDefinition: +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +Lint/NonAtomicFileOperation: Exclude: - - 'spec/unit/daemon/apns/feedback_receiver_spec.rb' + - 'lib/rpush/daemon/rpc/server.rb' -# Offense count: 5 -# Cop supports --auto-correct. +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). Lint/RedundantCopDisableDirective: Exclude: - - 'lib/rpush/client/active_model/gcm/notification.rb' + - 'lib/rpush/client/active_model/fcm/notification.rb' - 'lib/rpush/daemon/interruptible_sleep.rb' - 'lib/rpush/daemon/rpc/client.rb' - - 'lib/rpush/daemon/tcp_connection.rb' + - 'lib/rpush/daemon/store/active_record.rb' + - 'lib/rpush/daemon/store/redis.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Lint/RedundantDirGlobSort: + Exclude: + - 'spec/unit_spec_helper.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). Lint/RedundantRequireStatement: Exclude: - 'lib/rpush/daemon.rb' -# Offense count: 4 +# Offense count: 3 # Configuration parameters: AllowComments, AllowNil. Lint/SuppressedException: Exclude: - 'lib/rpush/daemon/interruptible_sleep.rb' - 'lib/rpush/daemon/rpc/client.rb' - - 'lib/rpush/daemon/tcp_connection.rb' + +# Offense count: 22 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: strict, consistent +Lint/SymbolConversion: + Exclude: + - 'spec/functional/fcm_spec.rb' + - 'spec/functional/retry_spec.rb' # Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'spec/functional/apns2_spec.rb' # Offense count: 3 -# Cop supports --auto-correct. -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. Lint/UnusedMethodArgument: Exclude: - 'lib/rpush/client/active_model/adm/notification.rb' - 'lib/rpush/client/active_model/apns/notification.rb' - - 'lib/rpush/client/active_model/gcm/notification.rb' + - 'lib/rpush/client/active_model/fcm/notification.rb' # Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'lib/rpush/daemon.rb' - 'lib/rpush/daemon/wns/post_request.rb' # Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect. Lint/UselessAssignment: Exclude: - 'spec/functional/apns2_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowComments. +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AutoCorrect. Lint/UselessMethodDefinition: Exclude: - 'lib/rpush/configuration.rb' -# Offense count: 83 -# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. -# IgnoredMethods: refine -Metrics/BlockLength: - Max: 326 - -# Offense count: 1 -# Configuration parameters: IgnoredMethods. -Metrics/PerceivedComplexity: - Max: 9 - # Offense count: 1 -# Cop supports --auto-correct. -Migration/DepartmentName: - Exclude: - - 'lib/rpush/daemon/tcp_connection.rb' +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. +# AllowedMethods: refine +Metrics/BlockLength: + Max: 26 # Offense count: 1 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. @@ -432,12 +510,13 @@ Naming/BlockParameterName: # Offense count: 1 # Configuration parameters: ForbiddenDelimiters. -# ForbiddenDelimiters: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$)) +# ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) Naming/HeredocDelimiterNaming: Exclude: - 'lib/rpush/daemon.rb' # Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: @@ -446,119 +525,565 @@ Naming/MemoizedInstanceVariableName: # Offense count: 1 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. -# AllowedNames: at, by, db, id, in, io, ip, of, on, os, pp, to +# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: Exclude: - 'lib/rpush/daemon/loggable.rb' # Offense count: 2 -# Configuration parameters: EnforcedStyle, AllowedIdentifiers. +# Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns. # SupportedStyles: snake_case, camelCase Naming/VariableName: Exclude: - 'lib/rpush/daemon/adm/delivery.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). Performance/AncestorsInclude: Exclude: - 'lib/rpush/configuration.rb' +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +Performance/MapCompact: + Exclude: + - 'lib/rpush/daemon/service_config_methods.rb' + - 'lib/rpush/daemon/store/redis.rb' + # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Performance/RedundantBlockCall: Exclude: - 'bm/bench.rb' -# Offense count: 5 -# Cop supports --auto-correct. +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). Performance/RegexpMatch: Exclude: - 'lib/rpush/client/active_model/apns/device_token_format_validator.rb' - 'lib/rpush/daemon/retry_header_parser.rb' - - 'lib/rpush/daemon/tcp_connection.rb' - 'spec/support/active_record_setup.rb' -# Offense count: 16 -# Cop supports --auto-correct. +# Offense count: 17 +# This cop supports safe autocorrection (--autocorrect). Performance/StringIdentifierArgument: Exclude: - 'lib/rpush/client/active_model/apns/notification.rb' + - 'lib/rpush/configuration.rb' - 'lib/rpush/daemon/loggable.rb' - 'lib/rpush/daemon/service_config_methods.rb' + - 'lib/rpush/deprecatable.rb' - 'lib/rpush/logger.rb' + - 'lib/rpush/plugin.rb' - 'spec/spec_helper.rb' - - 'spec/unit/daemon/apns/feedback_receiver_spec.rb' - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' - - 'spec/unit/daemon/tcp_connection_spec.rb' - 'spec/unit/logger_spec.rb' -# Offense count: 6 -# Cop supports --auto-correct. +# Offense count: 5 +# This cop supports unsafe autocorrection (--autocorrect-all). Performance/StringInclude: Exclude: - - 'lib/rpush/daemon/tcp_connection.rb' - 'lib/rpush/daemon/wns/post_request.rb' - 'spec/functional_spec_helper.rb' - 'spec/support/active_record_setup.rb' - 'spec/unit_spec_helper.rb' +# Offense count: 3 +RSpec/AnyInstance: + Exclude: + - 'spec/functional/fcm_spec.rb' + - 'spec/functional/retry_spec.rb' + - 'spec/unit/daemon/fcm/delivery_spec.rb' + +# Offense count: 28 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/BeEq: + Enabled: false + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: NegatedMatcher. +RSpec/ChangeByZero: + Exclude: + - 'spec/functional/apns2_spec.rb' + +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: be_a, be_kind_of +RSpec/ClassCheck: + Exclude: + - 'spec/functional/apns2_spec.rb' + - 'spec/unit/daemon_spec.rb' + +# Offense count: 7 +# Configuration parameters: Prefixes, AllowedPatterns. +# Prefixes: when, with, without +RSpec/ContextWording: + Exclude: + - 'spec/functional/fcm_priority_spec.rb' + - 'spec/unit/client/shared/app.rb' + - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' + - 'spec/unit/daemon/wns/post_request_spec.rb' + +# Offense count: 3 +# Configuration parameters: IgnoredMetadata. +RSpec/DescribeClass: + Exclude: + - 'spec/functional/apns2_spec.rb' + - 'spec/functional/embed_spec.rb' + - 'spec/functional/fcm_priority_spec.rb' + +# Offense count: 9 +RSpec/DescribeMethod: + Exclude: + - 'spec/unit/daemon/app_runner_spec.rb' + - 'spec/unit/daemon_spec.rb' + - 'spec/unit/embed_spec.rb' + - 'spec/unit/push_spec.rb' + +# Offense count: 118 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. +# SupportedStyles: described_class, explicit +RSpec/DescribedClass: + Enabled: false + # Offense count: 1 -# Cop supports --auto-correct. -Performance/TimesMap: +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AutoCorrect. +RSpec/EmptyExampleGroup: Exclude: - - 'spec/functional/apns_spec.rb' + - 'spec/unit/client/redis/apns2/app_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowConsecutiveOneLiners. +RSpec/EmptyLineAfterExample: + Exclude: + - 'spec/unit/client/shared/webpush/notification.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +RSpec/EmptyLineAfterFinalLet: + Exclude: + - 'spec/functional/webpush_spec.rb' + - 'spec/unit/client/shared/webpush/notification.rb' + - 'spec/unit/daemon/pushy/delivery_spec.rb' + - 'spec/unit/daemon/webpush/delivery_spec.rb' + +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowConsecutiveOneLiners. +RSpec/EmptyLineAfterHook: + Exclude: + - 'spec/unit/daemon/wns/delivery_spec.rb' + - 'spec/unit/daemon/wpns/delivery_spec.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +RSpec/EmptyLineAfterSubject: + Exclude: + - 'spec/unit/client/shared/pushy/notification.rb' + - 'spec/unit/client/shared/webpush/notification.rb' + +# Offense count: 53 +# Configuration parameters: CountAsOne. +RSpec/ExampleLength: + Max: 25 + +# Offense count: 74 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. +# DisallowedExamples: works +RSpec/ExampleWording: + Enabled: false + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: method_call, block +RSpec/ExpectChange: + Exclude: + - 'spec/unit/daemon/pushy/delivery_spec.rb' + +# Offense count: 5 +RSpec/ExpectInHook: + Exclude: + - 'spec/functional/apns2_spec.rb' + - 'spec/unit/daemon/adm/delivery_spec.rb' + +# Offense count: 11 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: implicit, each, example +RSpec/HookArgument: + Exclude: + - 'spec/functional/apns2_spec.rb' + - 'spec/functional_spec_helper.rb' + - 'spec/spec_helper.rb' + - 'spec/unit/client/shared/adm/app.rb' + - 'spec/unit/daemon/shared/store.rb' + - 'spec/unit_spec_helper.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect. +RSpec/HooksBeforeExamples: + Exclude: + - 'spec/unit/daemon/feeder_spec.rb' + - 'spec/unit/daemon/store/active_record_spec.rb' + - 'spec/unit/daemon/store/redis_spec.rb' + +# Offense count: 19 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: single_line_only, single_statement_only, disallow, require_implicit +RSpec/ImplicitSubject: + Exclude: + - 'spec/unit/client/shared/pushy/app.rb' + - 'spec/unit/client/shared/pushy/notification.rb' + - 'spec/unit/client/shared/webpush/app.rb' + - 'spec/unit/client/shared/webpush/notification.rb' + +# Offense count: 4 +# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns. +RSpec/IndexedLet: + Exclude: + - 'spec/unit/daemon/apnsp8/delivery_spec.rb' + - 'spec/unit/daemon/batch_spec.rb' + +# Offense count: 11 +# Configuration parameters: AssignmentOnly. +RSpec/InstanceVariable: + Exclude: + - 'spec/functional/apns2_spec.rb' + - 'spec/unit/deprecatable_spec.rb' + - 'spec/unit/logger_spec.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +RSpec/LeadingSubject: + Exclude: + - 'spec/unit/client/shared/webpush/notification.rb' + - 'spec/unit/daemon/pushy/delivery_spec.rb' + - 'spec/unit/daemon/webpush/delivery_spec.rb' + +# Offense count: 7 +RSpec/LeakyConstantDeclaration: + Exclude: + - 'spec/unit/daemon/delivery_spec.rb' + - 'spec/unit/daemon/service_config_methods_spec.rb' + - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' + - 'spec/unit/daemon_spec.rb' + - 'spec/unit/deprecatable_spec.rb' + - 'spec/unit/reflectable_spec.rb' + +# Offense count: 12 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect. +RSpec/LetBeforeExamples: + Exclude: + - 'spec/unit/client/active_record/fcm/notification_spec.rb' + - 'spec/unit/client/active_record/wns/raw_notification_spec.rb' + - 'spec/unit/daemon/store/active_record_spec.rb' + - 'spec/unit/daemon/store/redis_spec.rb' + +# Offense count: 244 +# Configuration parameters: EnforcedStyle. +# SupportedStyles: have_received, receive +RSpec/MessageSpies: + Enabled: false + +# Offense count: 4 +RSpec/MultipleDescribes: + Exclude: + - 'spec/unit/configuration_spec.rb' + - 'spec/unit/daemon/app_runner_spec.rb' + - 'spec/unit/embed_spec.rb' + - 'spec/unit/reflection_collection_spec.rb' + +# Offense count: 121 +RSpec/MultipleExpectations: + Max: 13 + +# Offense count: 94 +# Configuration parameters: AllowSubject. +RSpec/MultipleMemoizedHelpers: + Max: 14 + +# Offense count: 4 +# Configuration parameters: EnforcedStyle, IgnoreSharedExamples. +# SupportedStyles: always, named_only +RSpec/NamedSubject: + Exclude: + - 'spec/functional/cli_spec.rb' + - 'spec/unit/daemon/pushy/delivery_spec.rb' + +# Offense count: 3 +# Configuration parameters: AllowedGroups. +RSpec/NestedGroups: + Max: 4 + +# Offense count: 5 +# Configuration parameters: AllowedPatterns. +# AllowedPatterns: ^expect_, ^assert_ +RSpec/NoExpectationExample: + Exclude: + - 'spec/functional/synchronization_spec.rb' + - 'spec/unit/daemon/apns/certificate_expired_error_spec.rb' + - 'spec/unit/daemon/apnsp8/delivery_spec.rb' + - 'spec/unit/daemon/app_runner_spec.rb' + +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: not_to, to_not +RSpec/NotToNot: + Exclude: + - 'spec/unit/client/shared/apns/notification.rb' + - 'spec/unit/daemon/adm/delivery_spec.rb' + - 'spec/unit/daemon_spec.rb' + +# Offense count: 7 +RSpec/PendingWithoutReason: + Exclude: + - 'spec/unit/client/redis/apns/notification_spec.rb' + - 'spec/unit/client/redis/apns2/notification_spec.rb' + - 'spec/unit/client/redis/wns/raw_notification_spec.rb' + +# Offense count: 37 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers. +# SupportedStyles: inflected, explicit +RSpec/PredicateMatcher: + Exclude: + - 'spec/unit/client/active_record/apns/notification_spec.rb' + - 'spec/unit/client/active_record/apns2/notification_spec.rb' + - 'spec/unit/client/active_record/apnsp8/notification_spec.rb' + - 'spec/unit/client/redis/apns/notification_spec.rb' + - 'spec/unit/client/redis/apns2/notification_spec.rb' + - 'spec/unit/client/redis/apnsp8/notification_spec.rb' + - 'spec/unit/client/shared/apns/feedback.rb' + - 'spec/unit/client/shared/apns/notification.rb' + - 'spec/unit/client/shared/fcm/notification.rb' + - 'spec/unit/daemon/shared/store.rb' + +# Offense count: 4 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/ReceiveMessages: + Exclude: + - 'spec/functional/fcm_spec.rb' + - 'spec/functional/retry_spec.rb' + +# Offense count: 2 +RSpec/RepeatedExampleGroupDescription: + Exclude: + - 'spec/unit/daemon/batch_spec.rb' + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: and_return, block +RSpec/ReturnFromStub: + Exclude: + - 'spec/functional/cli_spec.rb' + - 'spec/spec_helper.rb' + - 'spec/unit/client/active_record/wns/raw_notification_spec.rb' + - 'spec/unit/client/redis/wns/raw_notification_spec.rb' + - 'spec/unit/client/shared/wns/raw_notification.rb' + - 'spec/unit/daemon_spec.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. +RSpec/ScatteredLet: + Exclude: + - 'spec/functional/embed_spec.rb' + +# Offense count: 26 +# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. +# Include: **/*_spec.rb +RSpec/SpecFilePathFormat: + Enabled: false + +# Offense count: 26 +RSpec/StubbedMock: + Exclude: + - 'spec/functional/adm_spec.rb' + - 'spec/functional/apns2_spec.rb' + - 'spec/functional/cli_spec.rb' + - 'spec/functional/fcm_spec.rb' + - 'spec/unit/daemon/adm/delivery_spec.rb' + - 'spec/unit/daemon/app_runner_spec.rb' + - 'spec/unit/daemon/dispatcher/http_spec.rb' + - 'spec/unit/daemon/service_config_methods_spec.rb' + - 'spec/unit/embed_spec.rb' + - 'spec/unit/logger_spec.rb' + +# Offense count: 14 +RSpec/SubjectStub: + Exclude: + - 'spec/functional/cli_spec.rb' + - 'spec/unit/daemon/pushy/delivery_spec.rb' + - 'spec/unit/daemon/webpush/delivery_spec.rb' + +# Offense count: 12 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: . +# SupportedStyles: constant, string +RSpec/VerifiedDoubleReference: + EnforcedStyle: string + +# Offense count: 99 +# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. +RSpec/VerifiedDoubles: + Enabled: false + +# Offense count: 3 +# This cop supports unsafe autocorrection (--autocorrect-all). +Rails/ApplicationRecord: + Exclude: + - 'lib/rpush/client/active_record/apns/feedback.rb' + - 'lib/rpush/client/active_record/app.rb' + - 'lib/rpush/client/active_record/notification.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent. +Rails/Blank: + Exclude: + - 'lib/rpush/daemon/apns2/delivery.rb' + - 'lib/rpush/daemon/apnsp8/delivery.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforceForPrefixed. +Rails/Delegate: + Exclude: + - 'spec/unit/daemon/adm/delivery_spec.rb' + - 'spec/unit/daemon/fcm/delivery_spec.rb' + - 'spec/unit/daemon/wns/delivery_spec.rb' + - 'spec/unit/daemon/wpns/delivery_spec.rb' + +# Offense count: 4 +# Configuration parameters: Include. +# Include: app/**/*.rb, config/**/*.rb, lib/**/*.rb +Rails/Exit: + Exclude: + - 'lib/rpush/cli.rb' + - 'lib/rpush/daemon.rb' + +# Offense count: 3 +# This cop supports unsafe autocorrection (--autocorrect-all). +Rails/NegateInclude: + Exclude: + - 'lib/rpush/daemon/adm/delivery.rb' + - 'lib/rpush/reflection_collection.rb' + +# Offense count: 21 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Include. +# Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb +Rails/Output: + Exclude: + - 'lib/rpush/cli.rb' + - 'lib/rpush/client/redis.rb' + - 'lib/rpush/daemon.rb' + - 'lib/rpush/daemon/app_runner.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank. +Rails/Present: + Exclude: + - 'lib/rpush/daemon.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Include. +# Include: **/Rakefile, **/*.rake +Rails/RakeEnvironment: + Exclude: + - 'lib/tasks/test.rake' + +# Offense count: 5 +# Configuration parameters: ForbiddenMethods, AllowedMethods. +# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all +Rails/SkipsModelValidations: + Exclude: + - 'lib/rpush/daemon/store/active_record.rb' + - 'spec/unit/daemon/fcm/delivery_spec.rb' + +# Offense count: 82 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: strict, flexible +Rails/TimeZone: + Enabled: false + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: to_fs, to_formatted_s +Rails/ToFormattedS: + Exclude: + - 'lib/rpush/logger.rb' + - 'spec/unit/logger_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +Rails/WhereNot: + Exclude: + - 'spec/unit/daemon/wns/delivery_spec.rb' + - 'spec/unit/daemon/wpns/delivery_spec.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). Security/JSONLoad: Exclude: - 'lib/rpush/daemon/rpc/server.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). Security/YAMLLoad: Exclude: - 'spec/support/active_record_setup.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: prefer_alias, prefer_alias_method Style/Alias: Exclude: - 'lib/rpush/daemon/ring_buffer.rb' -# Offense count: 9 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. +# Offense count: 13 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch -# IgnoredMethods: lambda, proc, it +# AllowedMethods: lambda, proc, it Style/BlockDelimiters: Exclude: - 'spec/functional/apns2_spec.rb' + - 'spec/functional/cli_spec.rb' + - 'spec/functional/embed_spec.rb' - 'spec/functional/webpush_spec.rb' - 'spec/unit/daemon/webpush/delivery_spec.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/CaseLikeIf: - Exclude: - - 'lib/rpush/cli.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: Keywords. +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Keywords, RequireColon. # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE Style/CommentAnnotation: Exclude: - 'lib/rpush/daemon/apnsp8/delivery.rb' + - 'spec/unit/client/shared/fcm/notification.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition Style/ConditionalAssignment: @@ -572,60 +1097,80 @@ Style/DocumentDynamicEvalDefinition: - 'lib/rpush/deprecatable.rb' - 'lib/rpush/reflection_collection.rb' +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/EmptyCaseCondition: + Exclude: + - 'lib/rpush/client/active_model/fcm/notification.rb' + # Offense count: 5 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Exclude: - 'lib/rpush/daemon/store/redis.rb' - 'spec/unit/daemon/app_runner_spec.rb' -# Offense count: 5 -# Cop supports --auto-correct. +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). Style/Encoding: Exclude: - 'lib/rpush/cli.rb' - 'lib/rpush/daemon.rb' - - 'lib/rpush/daemon/apns/feedback_receiver.rb' - 'lib/rpush/daemon/app_runner.rb' - 'rpush.gemspec' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/EvalWithLocation: Exclude: - 'lib/rpush/deprecatable.rb' - 'lib/rpush/reflection_collection.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/ExpandPathArguments: Exclude: - 'rpush.gemspec' -# Offense count: 2 -# Cop supports --auto-correct. +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). Style/ExplicitBlockArgument: Exclude: - - 'lib/rpush/daemon/gcm/delivery.rb' - 'lib/rpush/daemon/store/active_record/reconnectable.rb' +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowedVars. +Style/FetchEnvVar: + Exclude: + - 'lib/rpush/daemon/fcm/delivery.rb' + - 'spec/spec_helper.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/FileWrite: + Exclude: + - 'lib/tasks/test.rake' + # Offense count: 3 -# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, IgnoredMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns. # SupportedStyles: annotated, template, unannotated +# AllowedMethods: redirect Style/FormatStringToken: EnforcedStyle: unannotated -# Offense count: 279 -# Cop supports --auto-correct. +# Offense count: 266 +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 24 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). Style/GlobalStdStream: Exclude: - 'lib/rpush/cli.rb' @@ -637,87 +1182,107 @@ Style/GlobalStdStream: - 'spec/unit/deprecation_spec.rb' - 'spec/unit/logger_spec.rb' -# Offense count: 7 -# Configuration parameters: MinBodyLength. +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: Exclude: - 'lib/rpush/client/active_model/certificate_private_key_validator.rb' - 'lib/rpush/daemon.rb' - 'lib/rpush/daemon/adm/delivery.rb' - 'lib/rpush/daemon/app_runner.rb' - - 'lib/rpush/daemon/tcp_connection.rb' - - 'spec/unit/daemon/apns/feedback_receiver_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowSplatArgument. Style/HashConversion: Exclude: - 'lib/rpush/daemon/store/active_record.rb' -# Offense count: 1 -# Cop supports --auto-correct. +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedReceivers. +# AllowedReceivers: Thread.current Style/HashEachMethods: Exclude: - 'lib/rpush/daemon/wns/post_request.rb' + - 'spec/spec_helper.rb' -# Offense count: 7 -# Cop supports --auto-correct. +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/HashTransformKeys: + Exclude: + - 'lib/rpush/daemon/store/active_record.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Exclude: - 'lib/rpush/client/active_model/apns/notification.rb' - 'lib/rpush/client/active_model/webpush/app.rb' - 'lib/rpush/daemon/delivery.rb' - - 'lib/rpush/daemon/tcp_connection.rb' - 'lib/rpush/embed.rb' - 'spec/support/simplecov_helper.rb' +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedMethods. +# AllowedMethods: nonzero? +Style/IfWithBooleanLiteralBranches: + Exclude: + - 'lib/rpush/client/active_model/fcm/notification.rb' + # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: InverseMethods, InverseBlocks. Style/InverseMethods: Exclude: - 'lib/rpush/daemon/adm/delivery.rb' # Offense count: 46 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/MultilineIfModifier: Enabled: false # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowMethodComparison, ComparisonsThreshold. Style/MultipleComparison: Exclude: - 'lib/rpush/client/active_model/apns/notification.rb' -# Offense count: 34 -# Cop supports --auto-correct. +# Offense count: 28 +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Enabled: false # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/NegatedIfElseCondition: Exclude: - 'lib/rpush/cli.rb' -# Offense count: 11 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, IgnoredMethods. +# Offense count: 8 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - - 'spec/**/*' - 'lib/rpush/daemon.rb' - 'lib/rpush/daemon/apnsp8/delivery.rb' - 'lib/rpush/daemon/app_runner.rb' - - 'lib/rpush/daemon/dispatcher/apns_tcp.rb' - 'lib/rpush/daemon/feeder.rb' - 'lib/rpush/daemon/store/redis.rb' - 'lib/rpush/daemon/synchronizer.rb' +# Offense count: 2 +Style/OpenStructUse: + Exclude: + - 'lib/rpush/configuration.rb' + - 'lib/rpush/plugin.rb' + # Offense count: 7 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? @@ -728,96 +1293,171 @@ Style/OptionalBooleanParameter: - 'lib/tasks/test.rake' # Offense count: 3 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/OrAssignment: Exclude: - 'lib/rpush/daemon/wns/delivery.rb' - 'lib/rpush/daemon/wpns/delivery.rb' -# Offense count: 15 -# Cop supports --auto-correct. +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Exclude: - 'lib/rpush/client/active_model/apns/app.rb' - 'lib/rpush/client/active_model/apns2/app.rb' - 'lib/rpush/client/active_model/apnsp8/app.rb' - - 'lib/rpush/daemon/gcm/delivery.rb' - 'lib/rpush/daemon/signal_handler.rb' - 'spec/unit/daemon/adm/delivery_spec.rb' - - 'spec/unit/daemon/gcm/delivery_spec.rb' -# Offense count: 4 -# Cop supports --auto-correct. -Style/RedundantRegexpCharacterClass: +# Offense count: 20 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: same_as_string_literals, single_quotes, double_quotes +Style/QuotedSymbols: Exclude: - - 'lib/rpush/client/active_model/wns/notification.rb' - - 'lib/rpush/client/active_model/wpns/notification.rb' - - 'spec/unit/daemon/gcm/delivery_spec.rb' + - 'spec/functional/fcm_spec.rb' + - 'spec/functional/retry_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. -Style/RedundantSelf: +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantArrayConstructor: Exclude: - - 'lib/rpush/client/active_model/apns/notification.rb' + - 'lib/rpush/daemon/service_config_methods.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantBegin: + Exclude: + - 'lib/rpush/cli.rb' + - 'lib/rpush/daemon/batch.rb' + - 'lib/rpush/daemon/rpc/server.rb' + - 'lib/rpush/daemon/store/active_record/reconnectable.rb' + - 'lib/rpush/reflectable.rb' + - 'spec/support/active_record_setup.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantConditional: + Exclude: + - 'lib/rpush/client/active_model/fcm/notification.rb' + +# Offense count: 15 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantConstantBase: + Exclude: + - 'lib/rpush/daemon/store/active_record/reconnectable.rb' + - 'spec/functional/fcm_spec.rb' + - 'spec/functional/retry_spec.rb' + - 'spec/functional_spec_helper.rb' + - 'spec/spec_helper.rb' + - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' + - 'spec/unit/logger_spec.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantLineContinuation: + Exclude: + - 'lib/rpush/daemon/delivery_error.rb' + - 'lib/rpush/daemon/wns/toast_request.rb' + - 'lib/rpush/daemon/wpns/delivery.rb' + +# Offense count: 10 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantRegexpArgument: + Exclude: + - 'lib/rpush/daemon/wns/toast_request.rb' + - 'lib/rpush/daemon/wpns/delivery.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, explicit Style/RescueStandardError: Exclude: - 'lib/rpush/client/active_model/webpush/app.rb' -# Offense count: 17 -# Cop supports --auto-correct. -# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedMethods, AllowedPatterns. +Style/ReturnNilInPredicateMethodDefinition: + Exclude: + - 'spec/unit/daemon/store/active_record/reconnectable_spec.rb' + +# Offense count: 10 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/rpush/client/active_model/apns/notification.rb' - - 'lib/rpush/daemon/apns/feedback_receiver.rb' - 'lib/rpush/daemon/app_runner.rb' - - 'lib/rpush/daemon/dispatcher/apns_tcp.rb' - - 'lib/rpush/daemon/dispatcher/tcp.rb' - 'lib/rpush/daemon/dispatcher_loop.rb' - 'lib/rpush/daemon/feeder.rb' - 'lib/rpush/daemon/interruptible_sleep.rb' - 'lib/rpush/daemon/rpc/server.rb' - 'lib/rpush/daemon/signal_handler.rb' - - 'lib/rpush/daemon/tcp_connection.rb' - 'lib/rpush/embed.rb' - 'lib/rpush/logger.rb' # Offense count: 3 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Exclude: - 'spec/functional/apns2_spec.rb' +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/SlicingWithRange: + Exclude: + - 'spec/unit/client/shared/apns/notification.rb' + # Offense count: 11 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Mode. Style/StringConcatenation: Exclude: - 'lib/rpush/cli.rb' - 'lib/rpush/daemon/adm/delivery.rb' - - 'lib/rpush/daemon/gcm/delivery.rb' + - 'lib/rpush/daemon/fcm/delivery.rb' - 'lib/rpush/daemon/wns/delivery.rb' - 'lib/rpush/daemon/wpns/delivery.rb' - 'lib/rpush/deprecation.rb' - 'spec/support/active_record_setup.rb' -# Offense count: 11 -# Cop supports --auto-correct. +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiteralsInInterpolation: + Exclude: + - 'spec/unit/logger_spec.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +Style/SuperArguments: + Exclude: + - 'lib/rpush/client/active_model/fcm/notification.rb' + - 'lib/rpush/client/active_model/webpush/notification.rb' + - 'lib/rpush/configuration.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/SuperWithArgsParentheses: + Exclude: + - 'lib/rpush/client/active_model/webpush/notification.rb' + +# Offense count: 10 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: . # SupportedStyles: percent, brackets Style/SymbolArray: EnforcedStyle: percent - MinSize: 21 + MinSize: 20 # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, AllowSafeAssignment. # SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex Style/TernaryParentheses: @@ -826,7 +1466,7 @@ Style/TernaryParentheses: - 'lib/rpush/daemon/store/active_record.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInArrayLiteral: @@ -834,27 +1474,26 @@ Style/TrailingCommaInArrayLiteral: - 'spec/unit/client/shared/webpush/app.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/UnlessElse: Exclude: - 'lib/rpush/daemon/store/active_record.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/WhileUntilDo: Exclude: - 'lib/rpush/daemon/apnsp8/delivery.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/WhileUntilModifier: Exclude: - 'lib/rpush/daemon/apnsp8/delivery.rb' - 'spec/functional/synchronization_spec.rb' -# Offense count: 4 -# Cop supports --auto-correct. +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Exclude: - 'lib/rpush/daemon/app_runner.rb' - - 'lib/rpush/daemon/dispatcher/apns_tcp.rb' diff --git a/.ruby-version b/.ruby-version index fd2a01863..351227fca 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.1.0 +3.2.4 diff --git a/Appraisals b/Appraisals index 357705d6f..9931f8187 100644 --- a/Appraisals +++ b/Appraisals @@ -33,3 +33,19 @@ appraise "rails-7.0" do gem "rails", "~> 7.0.0" end end + +appraise "rails-7.1" do + gem "activesupport", "~> 7.1.0" + + group :development do + gem "rails", "~> 7.1.0" + end +end + +appraise "rails-7.2" do + gem "activesupport", "~> 7.2.0" + + group :development do + gem "rails", "~> 7.2.0" + end +end diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b2bee5b6..637941a85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,51 @@ ## [Unreleased](https://github.com/rpush/rpush/tree/HEAD) +[Full Changelog](https://github.com/rpush/rpush/compare/v9.2.0...HEAD) + +## [v9.2.0](https://github.com/rpush/rpush/tree/v9.2.0) (2024-11-28) + +* Support for Rails 7.2 [\#706](https://github.com/rpush/rpush/pull/706) ([benlangfeld](https://github.com/benlangfeld)) + +[Full Changelog](https://github.com/rpush/rpush/compare/v9.1.0...v9.2.0) + +## [v9.1.0](https://github.com/rpush/rpush/tree/v9.1.0) (2024-09-24) + +**Merged pull requests:** + +* Add support for FSM iOS badges [\#704](https://github.com/rpush/rpush/pull/704) ([WoutDev](https://github.com/WoutDev)) +* Support for Rails 7.1 [\#675](https://github.com/rpush/rpush/pull/675) ([benlangfeld](https://github.com/benlangfeld)) +* Enable running tests on sqlite3 [\#705](https://github.com/rpush/rpush/pull/705) ([benlangfeld](https://github.com/benlangfeld)) + +[Full Changelog](https://github.com/rpush/rpush/compare/v9.0.0...v9.1.0) + +## [v9.0.0](https://github.com/rpush/rpush/tree/v9.0.0) (2024-09-09) + +**Merged pull requests:** + +* Support for Ruby 3.2 & 3.3 [\#679](https://github.com/rpush/rpush/pull/679) ([benlangfeld](https://github.com/benlangfeld)) + +**Breaking:** + +* Removed legacy APNSv1 implementation (Apple binary protocol) since this was shut down in 2021. [\#680](https://github.com/rpush/rpush/pull/680) ([benlangfeld](https://github.com/benlangfeld)) +* Removed legacy GCM implementation since this was shut down by Google in August 2024 and replaced by FCM (supported in RPush 8.0.0) [\#688](https://github.com/rpush/rpush/pull/688) ([benlangfeld](https://github.com/benlangfeld)) +* Drop support for Ruby 2.x [\#672](https://github.com/rpush/rpush/pull/672) ([benlangfeld](https://github.com/benlangfeld)) + +[Full Changelog](https://github.com/rpush/rpush/compare/v8.0.0...v9.0.0) + +## [v8.0.0](https://github.com/rpush/rpush/tree/v8.0.0) (2024-09-06) + **Merged pull requests:** * Support for FCMv1 [\#620](https://github.com/rpush/rpush/pull/620) ([mirkode](https://github.com/mirkode)), [\#660](https://github.com/rpush/rpush/pull/660) ([AnilRh](https://github.com/AnilRh)) and [\#673](https://github.com/rpush/rpush/pull/673) ([SixiS](https://github.com/SixiS), [Henridv](https://github.com/Henridv) & [benlangfeld](https://github.com/benlangfeld)) +* No longer silence content-available notifications for APNs. Reverts the following change from the v6.0.0 release. See https://github.com/rpush/rpush/issues/647. + * Fix silent APNS notifications for Apns2 and Apnsp8 [\#596](https://github.com/rpush/rpush/pull/596) ([shved270189](https://github.com/shved270189)) **Breaking:** * Dropped support for Ruby 2.4, 2.5, 2.6 and Rails 5.2. -[Full Changelog](https://github.com/rpush/rpush/compare/v7.0.1...HEAD) +[Full Changelog](https://github.com/rpush/rpush/compare/v7.0.1...v8.0.0) ## [v7.0.1](https://github.com/rpush/rpush/tree/v7.0.1) (2022-03-02) diff --git a/Gemfile.lock b/Gemfile.lock index fd44332d4..8b54b3d56 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ PATH remote: . specs: - rpush (7.1.0) - activesupport (>= 5.2, < 7.1.0) + rpush (9.2.0) + activesupport (>= 6.0, != 7.2.1, != 7.2.0, != 7.1.4) googleauth jwt (>= 1.5.6) multi_json (~> 1.0) @@ -39,15 +39,15 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) appraisal (2.4.1) bundler rake thor (>= 0.14.0) ast (2.4.2) base64 (0.2.0) - builder (3.2.4) + builder (3.3.0) byebug (11.1.3) codeclimate-test-reporter (1.0.7) simplecov @@ -65,33 +65,39 @@ GEM reline (>= 0.3.8) diff-lcs (1.5.1) docile (1.4.0) - erubi (1.12.0) - faraday (2.9.0) - faraday-net_http (>= 2.0, < 3.2) - faraday-net_http (3.1.0) + erubi (1.13.0) + faraday (2.12.0) + faraday-net_http (>= 2.0, < 3.4) + json + logger + faraday-net_http (3.3.0) net-http - google-cloud-env (2.1.0) + google-cloud-env (2.2.1) faraday (>= 1.0, < 3.a) - googleauth (1.9.2) + googleauth (1.11.2) faraday (>= 1.0, < 3.a) google-cloud-env (~> 2.1) jwt (>= 1.4, < 3.0) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) - http-2 (0.11.0) + http-2 (0.12.0) + base64 i18n (1.14.5) concurrent-ruby (~> 1.0) io-console (0.7.2) irb (1.14.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - jwt (2.8.2) + json (2.7.2) + jwt (2.9.3) base64 - loofah (2.22.0) + language_server-protocol (3.17.0.3) + logger (1.6.1) + loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - method_source (1.0.0) + method_source (1.1.0) mini_portile2 (2.8.5) minitest (5.24.1) modis (4.3.0) @@ -105,24 +111,25 @@ GEM mysql2 (0.5.6) net-http (0.4.1) uri - net-http-persistent (4.0.2) + net-http-persistent (4.0.4) connection_pool (~> 2.2) net-http2 (0.18.5) http-2 (~> 0.11) - nokogiri (1.16.0) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) openssl (3.2.0) os (1.1.4) - parallel (1.21.0) - parser (3.1.0.0) + parallel (1.26.3) + parser (3.3.5.0) ast (~> 2.4.1) - pg (1.2.3) + racc + pg (1.5.9) psych (5.1.2) stringio - public_suffix (5.0.4) - racc (1.7.3) - rack (2.2.8) + public_suffix (6.0.1) + racc (1.8.1) + rack (2.2.9) rack-test (2.1.0) rack (>= 1.3) rails-dom-testing (2.2.0) @@ -147,10 +154,9 @@ GEM redis-client (>= 0.22.0) redis-client (0.22.2) connection_pool - regexp_parser (2.2.0) + regexp_parser (2.9.2) reline (0.5.9) io-console (~> 0.5) - rexml (3.2.5) rpush-redis (1.2.0) modis (>= 3.0, < 5.0) rspec (3.13.0) @@ -166,22 +172,32 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.1) - rubocop (1.12.1) + rubocop (1.66.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.0.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.2.0, < 2.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.15.1) - parser (>= 3.0.1.1) - rubocop-performance (1.13.2) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - ruby-progressbar (1.11.0) - signet (0.18.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.32.3) + parser (>= 3.3.1.0) + rubocop-performance (1.21.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.26.0) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-rspec (3.0.4) + rubocop (~> 1.61) + ruby-progressbar (1.13.0) + signet (0.19.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -192,7 +208,7 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) - sqlite3 (2.0.2) + sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) stackprof (0.2.17) stringio (3.1.1) @@ -200,12 +216,12 @@ GEM timecop (0.9.4) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.1.0) - uri (0.13.0) + unicode-display_width (2.5.0) + uri (0.13.1) web-push (3.0.1) jwt (~> 2.0) openssl (~> 3.0) - zeitwerk (2.6.12) + zeitwerk (2.7.1) PLATFORMS ruby @@ -223,12 +239,15 @@ DEPENDENCIES rpush! rpush-redis (~> 1.0) rspec - rubocop (~> 1.12.0) + rubocop (~> 1.66) rubocop-performance + rubocop-rails + rubocop-rake + rubocop-rspec simplecov - sqlite3 + sqlite3 (~> 1.4) stackprof timecop BUNDLED WITH - 2.3.5 + 2.5.23 diff --git a/README.md b/README.md index 2b1fde050..7f9b4bef0 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,9 @@ There is a choice of two modes (and one legacy mode) using certificates or using * `Rpush::Apns2` This requires an annually renewable certificate. see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_certificate-based_connection_to_apns * `Rpush::Apnsp8` This uses encrypted tokens and requires an encryption key id and encryption key (provide as a p8 file). (see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns) -* `Rpush::Apns` There is also the original APNS (the original version using certificates with a binary underlying protocol over TCP directly rather than over Http/2). Apple have [announced](https://developer.apple.com/news/?id=c88acm2b) that this is not supported after March 31, 2021. -If this is your first time using the APNs, you will need to generate either SSL certificates (for Apns2 or Apns) or an Encryption Key (p8) and an Encryption Key ID (for Apnsp8). See [Generating Certificates](https://github.com/rpush/rpush/wiki/Generating-Certificates) for instructions. +If this is your first time using the APNs, you will need to generate either SSL certificates (for standard Apns) or an Encryption Key (p8) and an Encryption Key ID (for Apnsp8). See [Generating Certificates](https://github.com/rpush/rpush/wiki/Generating-Certificates) for instructions. ##### Apnsp8 @@ -83,6 +82,7 @@ n = Rpush::Apnsp8::Notification.new n.app = Rpush::Apnsp8::App.find_by_name("ios_app") n.device_token = "..." # hex string n.alert = "hi mom!" +# n.alert = { title: "push title", subtitle: "more to say", body: "hi mom!" } n.data = { foo: :bar } n.save! ``` @@ -107,6 +107,7 @@ n = Rpush::Apns2::Notification.new n.app = Rpush::Apns2::App.find_by_name("ios_app") n.device_token = "..." # hex string n.alert = "hi mom!" +# n.alert = { title: "push title", subtitle: "more to say", body: "hi mom!" } n.data = { headers: { 'apns-topic': "BUNDLE ID" }, # the bundle id of the app, like com.example.appname. Not necessary if set on the app (see above) foo: :bar @@ -116,47 +117,24 @@ n.save! You should also implement the [ssl_certificate_will_expire](https://github.com/rpush/rpush/wiki/Reflection-API) reflection to monitor when your certificate is due to expire. -##### Apns (legacy protocol) - -```ruby -app = Rpush::Apns::App.new -app.name = "ios_app" -app.certificate = File.read("/path/to/sandbox.pem") -app.environment = "development" # APNs environment. -app.password = "certificate password" -app.connections = 1 -app.save! -``` - -```ruby -n = Rpush::Apns::Notification.new -n.app = Rpush::Apns::App.find_by_name("ios_app") -n.device_token = "..." # hex string -n.alert = "hi mom!" -n.data = { foo: :bar } -n.save! -``` - ##### Safari Push Notifications Using one of the notifications methods above, the `url_args` attribute is available for Safari Push Notifications. ##### Environment -The app `environment` for any Apns* option is "development" for XCode installs, and "production" for app store and TestFlight. Note that for Apns2 you can now use one (production + sandbox) certificate (you don't need a separate "sandbox" or development certificate), but if you do generate a development/sandbox certificate it can only be used for "development". With Apnsp8 tokens, you can target either "development" or "production" environments. +The app `environment` for any Apns* option is "development" for XCode installs, and "production" for app store and TestFlight. Note that you can now use one (production + sandbox) certificate (you don't need a separate "sandbox" or development certificate), but if you do generate a development/sandbox certificate it can only be used for "development". With Apnsp8 tokens, you can target either "development" or "production" environments. #### Firebase Cloud Messaging -##### Firebase Cloud Messaging API (V1) - You will need two params to make use of FCM via Rpush. -- `firebase_project_id` - The `Project ID` in your Firebase Project Settings +- `firebase_project_id` - The `Project number` in your Firebase Project Settings - `json_key` - The JSON key file for a service account with the `Firebase Admin SDK Administrator Service Agent` role. -Create service account in the google cloud account attached to your firebase account: -https://console.cloud.google.com/iam-admin/serviceaccounts -Make sure it has Role `Firebase Admin SDK Administrator Service Agent` -Add + Download the json key for the service account. +Create service account in the google cloud account attached to your firebase account: +https://console.cloud.google.com/iam-admin/serviceaccounts +Make sure it has Role `Firebase Admin SDK Administrator Service Agent` +Add + Download the json key for the service account. Once you have those two params, you can create an FCM app and send notifications. @@ -173,41 +151,11 @@ fcm_app.save! n = Rpush::Fcm::Notification.new n.app = Rpush::Fcm::App.where(name: "fcm_app").first n.device_token = device_token # Note that device_token is used here instead of registration_ids +n.notification = { title: "push title", body: "hi mom!" } # either title or body needs to be set, or nothing goes through n.data = {}.transform_values(&:to_s) # All values going in here have to be strings, if you have anything else - nothing goes through n.save! ``` -##### Cloud Messaging API (Legacy) - -**Note:** Deprecated on 2023/6/20 and scheduled to be disabled on 2024/6/20. - -FCM and GCM are – as of writing – compatible with each other. See also [this comment](https://github.com/rpush/rpush/issues/284#issuecomment-228330206) for further references. - -Please refer to the Firebase Console on where to find your `auth_key` (probably called _Server Key_ there). To verify you have the right key, use tools like [Postman](https://www.getpostman.com/), [HTTPie](https://httpie.org/), `curl` or similar before reporting a new issue. See also [this comment](https://github.com/rpush/rpush/issues/346#issuecomment-289218776). - -```ruby -app = Rpush::Gcm::App.new -app.name = "android_app" -app.auth_key = "..." -app.connections = 1 -app.save! -``` - -```ruby -n = Rpush::Gcm::Notification.new -n.app = Rpush::Gcm::App.find_by_name("android_app") -n.registration_ids = ["..."] -n.data = { message: "hi mom!" } -n.priority = 'high' # Optional, can be either 'normal' or 'high' -n.content_available = true # Optional -# Optional notification payload. See the reference below for more keys you can use! -n.notification = { body: 'great match!', - title: 'Portugal vs. Denmark', - icon: 'myicon' - } -n.save! -``` - FCM also requires you to respond to [Canonical IDs](https://github.com/rpush/rpush/wiki/Canonical-IDs). Check the [FCM reference](https://firebase.google.com/docs/cloud-messaging/http-server-ref#notification-payload-support) for what keys you can use and are available to you. **Note:** Not all are yet implemented in Rpush. @@ -408,7 +356,6 @@ Rpush will deliver all pending notifications and then exit. ```ruby Rpush.push -Rpush.apns_feedback ``` See [Push API](https://github.com/rpush/rpush/wiki/Push-API) for more details. diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile new file mode 100644 index 000000000..462cdd699 --- /dev/null +++ b/gemfiles/rails_7.1.gemfile @@ -0,0 +1,11 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.1.0" + +group :development do + gem "rails", "~> 7.1.0" +end + +gemspec path: "../" diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile new file mode 100644 index 000000000..b68f3f8ac --- /dev/null +++ b/gemfiles/rails_7.2.gemfile @@ -0,0 +1,11 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.2.0" + +group :development do + gem "rails", "~> 7.2.0" +end + +gemspec path: "../" diff --git a/lib/generators/templates/rpush.rb b/lib/generators/templates/rpush.rb index f102adc65..72d534ae1 100644 --- a/lib/generators/templates/rpush.rb +++ b/lib/generators/templates/rpush.rb @@ -30,9 +30,6 @@ # If the logger goes to stdout, you can disable foreground logging to avoid duplication. # config.foreground_logging = false - # config.apns.feedback_receiver.enabled = true - # config.apns.feedback_receiver.frequency = 60 - end Rpush.reflect do |on| @@ -76,10 +73,6 @@ # on.notification_id_will_retry do |app, notification_id, retry_after| # end - # Called when a TCP connection is lost and will be reconnected. - # on.tcp_connection_lost do |app, error| - # end - # Called for each recipient which successfully receives a notification. This # can occur more than once for the same notification when there are multiple # recipients. @@ -88,35 +81,30 @@ # Called for each recipient which fails to receive a notification. This # can occur more than once for the same notification when there are multiple - # recipients. (do not handle invalid registration IDs here) + # recipients. (do not handle invalid device tokens here) # on.fcm_failed_to_recipient do |notification, error| # end - # Called when the FCM returns a failure that indicates an invalid device token. - # You will need to delete the device token from your records. - # on.fcm_invalid_device_token do |app, error, device_token| - # end - # Called for each recipient which successfully receives a notification. This # can occur more than once for the same notification when there are multiple # recipients. - # on.gcm_delivered_to_recipient do |notification, registration_id| + # on.fcm_delivered_to_recipient do |notification, device_token| # end # Called for each recipient which fails to receive a notification. This # can occur more than once for the same notification when there are multiple - # recipients. (do not handle invalid registration IDs here) - # on.gcm_failed_to_recipient do |notification, error, registration_id| + # recipients. (do not handle invalid device tokens here) + # on.fcm_failed_to_recipient do |notification, error, device_token| # end - # Called when the GCM returns a canonical registration ID. + # Called when the FCM returns a canonical device token. # You will need to replace old_id with canonical_id in your records. - # on.gcm_canonical_id do |old_id, canonical_id| + # on.fcm_canonical_id do |old_id, canonical_id| # end - # Called when the GCM returns a failure that indicates an invalid registration id. - # You will need to delete the registration_id from your records. - # on.gcm_invalid_registration_id do |app, error, registration_id| + # Called when the FCM returns a failure that indicates an invalid device token. + # You will need to delete the device_token from your records. + # on.fcm_invalid_device_token do |app, error, device_token| # end # Called when an SSL certificate will expire within 1 month. diff --git a/lib/rpush.rb b/lib/rpush.rb index e913d30a8..d9d532984 100644 --- a/lib/rpush.rb +++ b/lib/rpush.rb @@ -20,7 +20,6 @@ require 'rpush/plugin' require 'rpush/embed' require 'rpush/push' -require 'rpush/apns_feedback' module Rpush def self.jruby? diff --git a/lib/rpush/apns_feedback.rb b/lib/rpush/apns_feedback.rb deleted file mode 100644 index defd81631..000000000 --- a/lib/rpush/apns_feedback.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Rpush - def self.apns_feedback - require 'rpush/daemon' - Rpush::Daemon.common_init - - Rpush::Apns::App.all.each do |app| - # Redis stores every App type on the same namespace, hence the - # additional filtering - next unless app.service_name == 'apns' - next unless app.feedback_enabled - - receiver = Rpush::Daemon::Apns::FeedbackReceiver.new(app) - receiver.check_for_feedback - end - - nil - end -end diff --git a/lib/rpush/client/active_model.rb b/lib/rpush/client/active_model.rb index 7b602ce01..6d41430a7 100644 --- a/lib/rpush/client/active_model.rb +++ b/lib/rpush/client/active_model.rb @@ -25,10 +25,6 @@ require 'rpush/client/active_model/fcm/app' require 'rpush/client/active_model/fcm/notification' -require 'rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator' -require 'rpush/client/active_model/gcm/app' -require 'rpush/client/active_model/gcm/notification' - require 'rpush/client/active_model/wpns/app' require 'rpush/client/active_model/wpns/notification' diff --git a/lib/rpush/client/active_model/apns/notification.rb b/lib/rpush/client/active_model/apns/notification.rb index 8b0847034..1b7aceffa 100644 --- a/lib/rpush/client/active_model/apns/notification.rb +++ b/lib/rpush/client/active_model/apns/notification.rb @@ -52,10 +52,6 @@ def content_available=(bool) self.data = (data || {}).merge(CONTENT_AVAILABLE_KEY => true) end - def content_available? - (self.data || {})[CONTENT_AVAILABLE_KEY] - end - def as_json(options = nil) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity json = ActiveSupport::OrderedHash.new diff --git a/lib/rpush/client/active_model/fcm/notification.rb b/lib/rpush/client/active_model/fcm/notification.rb index 876969238..c4c821df5 100644 --- a/lib/rpush/client/active_model/fcm/notification.rb +++ b/lib/rpush/client/active_model/fcm/notification.rb @@ -77,6 +77,7 @@ def apns_config aps['mutable-content'] = 1 if mutable_content aps['content-available'] = 1 if content_available aps['sound'] = 'default' if sound == 'default' + aps['badge'] = badge if badge json['payload']['aps'] = aps diff --git a/lib/rpush/client/active_model/gcm/app.rb b/lib/rpush/client/active_model/gcm/app.rb deleted file mode 100644 index 3a3929eaa..000000000 --- a/lib/rpush/client/active_model/gcm/app.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Rpush - module Client - module ActiveModel - module Gcm - module App - def self.included(base) - base.instance_eval do - validates :auth_key, presence: true - end - end - - def service_name - 'gcm' - end - end - end - end - end -end diff --git a/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb b/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb deleted file mode 100644 index c6f1b3b9d..000000000 --- a/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Rpush - module Client - module ActiveModel - module Gcm - class ExpiryCollapseKeyMutualInclusionValidator < ::ActiveModel::Validator - def validate(record) - return unless record.collapse_key && !record.expiry - record.errors.add :expiry, 'must be set when using a collapse_key' - end - end - end - end - end -end diff --git a/lib/rpush/client/active_model/gcm/notification.rb b/lib/rpush/client/active_model/gcm/notification.rb deleted file mode 100644 index 94f47c9d6..000000000 --- a/lib/rpush/client/active_model/gcm/notification.rb +++ /dev/null @@ -1,62 +0,0 @@ -module Rpush - module Client - module ActiveModel - module Gcm - module Notification - GCM_PRIORITY_HIGH = Rpush::Client::ActiveModel::Apns::Notification::APNS_PRIORITY_IMMEDIATE - GCM_PRIORITY_NORMAL = Rpush::Client::ActiveModel::Apns::Notification::APNS_PRIORITY_CONSERVE_POWER - GCM_PRIORITIES = [GCM_PRIORITY_HIGH, GCM_PRIORITY_NORMAL] - - def self.included(base) - base.instance_eval do - validates :registration_ids, presence: true - validates :priority, inclusion: { in: GCM_PRIORITIES }, allow_nil: true - validates :dry_run, inclusion: { in: [true, false] } - - validates_with Rpush::Client::ActiveModel::PayloadDataSizeValidator, limit: 4096 - validates_with Rpush::Client::ActiveModel::RegistrationIdsCountValidator, limit: 1000 - - validates_with Rpush::Client::ActiveModel::Gcm::ExpiryCollapseKeyMutualInclusionValidator - end - end - - # This is a hack. The schema defines `priority` to be an integer, but GCM expects a string. - # But for users of rpush to have an API they might expect (setting priority to `high`, not 10) - # we do a little conversion here. - # I'm not happy about it, but this will have to do until I can take a further look. - def priority=(priority) - case priority - when 'high', GCM_PRIORITY_HIGH - super(GCM_PRIORITY_HIGH) - when 'normal', GCM_PRIORITY_NORMAL - super(GCM_PRIORITY_NORMAL) - else - errors.add(:priority, 'must be one of either "normal" or "high"') - end - end - - def as_json(options = nil) # rubocop:disable Metrics/PerceivedComplexity - json = { - 'registration_ids' => registration_ids, - 'delay_while_idle' => delay_while_idle, - 'data' => data - } - json['collapse_key'] = collapse_key if collapse_key - json['content_available'] = content_available if content_available - json['mutable_content'] = mutable_content if mutable_content - json['dry_run'] = dry_run if dry_run - json['notification'] = notification if notification - json['priority'] = priority_for_notification if priority - json['time_to_live'] = expiry if expiry - json - end - - def priority_for_notification - return 'high' if priority == GCM_PRIORITY_HIGH - 'normal' if priority == GCM_PRIORITY_NORMAL - end - end - end - end - end -end diff --git a/lib/rpush/client/active_record.rb b/lib/rpush/client/active_record.rb index 6e6678ad4..af8652826 100644 --- a/lib/rpush/client/active_record.rb +++ b/lib/rpush/client/active_record.rb @@ -19,9 +19,6 @@ require 'rpush/client/active_record/fcm/notification' require 'rpush/client/active_record/fcm/app' -require 'rpush/client/active_record/gcm/notification' -require 'rpush/client/active_record/gcm/app' - require 'rpush/client/active_record/wpns/notification' require 'rpush/client/active_record/wpns/app' diff --git a/lib/rpush/client/active_record/gcm/app.rb b/lib/rpush/client/active_record/gcm/app.rb deleted file mode 100644 index a3ac05a40..000000000 --- a/lib/rpush/client/active_record/gcm/app.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Rpush - module Client - module ActiveRecord - module Gcm - class App < Rpush::Client::ActiveRecord::App - include Rpush::Client::ActiveModel::Gcm::App - end - end - end - end -end diff --git a/lib/rpush/client/active_record/gcm/notification.rb b/lib/rpush/client/active_record/gcm/notification.rb deleted file mode 100644 index 294dcf34e..000000000 --- a/lib/rpush/client/active_record/gcm/notification.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Rpush - module Client - module ActiveRecord - module Gcm - class Notification < Rpush::Client::ActiveRecord::Notification - include Rpush::Client::ActiveModel::Gcm::Notification - end - end - end - end -end diff --git a/lib/rpush/client/active_record/notification.rb b/lib/rpush/client/active_record/notification.rb index 666c2fc7b..eff86b70f 100644 --- a/lib/rpush/client/active_record/notification.rb +++ b/lib/rpush/client/active_record/notification.rb @@ -7,6 +7,8 @@ class Notification < ::ActiveRecord::Base self.table_name = 'rpush_notifications' + self.default_column_serializer = YAML if respond_to?(:default_column_serializer) + serialize :registration_ids serialize :url_args diff --git a/lib/rpush/client/redis.rb b/lib/rpush/client/redis.rb index f47b237be..f9070133c 100644 --- a/lib/rpush/client/redis.rb +++ b/lib/rpush/client/redis.rb @@ -30,9 +30,6 @@ require 'rpush/client/redis/fcm/app' require 'rpush/client/redis/fcm/notification' -require 'rpush/client/redis/gcm/app' -require 'rpush/client/redis/gcm/notification' - require 'rpush/client/redis/adm/app' require 'rpush/client/redis/adm/notification' diff --git a/lib/rpush/client/redis/gcm/app.rb b/lib/rpush/client/redis/gcm/app.rb deleted file mode 100644 index 2bd8b9fa3..000000000 --- a/lib/rpush/client/redis/gcm/app.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Rpush - module Client - module Redis - module Gcm - class App < Rpush::Client::Redis::App - include Rpush::Client::ActiveModel::Gcm::App - end - end - end - end -end diff --git a/lib/rpush/client/redis/gcm/notification.rb b/lib/rpush/client/redis/gcm/notification.rb deleted file mode 100644 index cb7ce5a0c..000000000 --- a/lib/rpush/client/redis/gcm/notification.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Rpush - module Client - module Redis - module Gcm - class Notification < Rpush::Client::Redis::Notification - include Rpush::Client::ActiveModel::Gcm::Notification - end - end - end - end -end diff --git a/lib/rpush/configuration.rb b/lib/rpush/configuration.rb index 77ddcad1e..4e72133dc 100644 --- a/lib/rpush/configuration.rb +++ b/lib/rpush/configuration.rb @@ -16,27 +16,12 @@ def configure end end - CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :foreground_logging, :log_level, :plugin, :apns] + CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :foreground_logging, :log_level, :plugin] DEPRECATED_ATTRS = [] CONFIG_ATTRS = CURRENT_ATTRS + DEPRECATED_ATTRS class ConfigurationError < StandardError; end - class ApnsFeedbackReceiverConfiguration < Struct.new(:frequency, :enabled) # rubocop:disable Style/StructInheritance - def initialize - super - self.enabled = true - self.frequency = 60 - end - end - - class ApnsConfiguration < Struct.new(:feedback_receiver) # rubocop:disable Style/StructInheritance - def initialize - super - self.feedback_receiver = ApnsFeedbackReceiverConfiguration.new - end - end - class Configuration < Struct.new(*CONFIG_ATTRS) # rubocop:disable Style/StructInheritance include Deprecatable @@ -55,8 +40,6 @@ def initialize self.foreground = false self.foreground_logging = true - self.apns = ApnsConfiguration.new - # Internal options. self.embedded = false self.push = false @@ -106,7 +89,7 @@ def initialize_client client_module = Rpush::Client.const_get(client.to_s.camelize) Rpush.send(:include, client_module) unless Rpush.ancestors.include?(client_module) - [:Apns, :Fcm, :Gcm, :Wpns, :Wns, :Adm, :Pushy, :Webpush].each do |service| + [:Apns, :Fcm, :Wpns, :Wns, :Adm, :Pushy, :Webpush].each do |service| Rpush.const_set(service, client_module.const_get(service)) unless Rpush.const_defined?(service) end diff --git a/lib/rpush/daemon.rb b/lib/rpush/daemon.rb index 274375dd4..0a5279001 100644 --- a/lib/rpush/daemon.rb +++ b/lib/rpush/daemon.rb @@ -19,11 +19,8 @@ require 'rpush/daemon/queue_payload' require 'rpush/daemon/synchronizer' require 'rpush/daemon/app_runner' -require 'rpush/daemon/tcp_connection' require 'rpush/daemon/dispatcher_loop' require 'rpush/daemon/dispatcher/http' -require 'rpush/daemon/dispatcher/tcp' -require 'rpush/daemon/dispatcher/apns_tcp' require 'rpush/daemon/dispatcher/apns_http2' require 'rpush/daemon/dispatcher/apnsp8_http2' require 'rpush/daemon/service_config_methods' @@ -38,10 +35,6 @@ require 'rpush/daemon/store/interface' -require 'rpush/daemon/apns/delivery' -require 'rpush/daemon/apns/feedback_receiver' -require 'rpush/daemon/apns' - require 'rpush/daemon/apns2/delivery' require 'rpush/daemon/apns2' @@ -53,9 +46,6 @@ require 'rpush/daemon/fcm' require 'rpush/daemon/google_credential_cache' -require 'rpush/daemon/gcm/delivery' -require 'rpush/daemon/gcm' - require 'rpush/daemon/wpns/delivery' require 'rpush/daemon/wpns' diff --git a/lib/rpush/daemon/apns.rb b/lib/rpush/daemon/apns.rb deleted file mode 100644 index 1b9e0acf7..000000000 --- a/lib/rpush/daemon/apns.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Rpush - module Daemon - module Apns - extend ServiceConfigMethods - - HOSTS = { - production: ['gateway.push.apple.com', 2195], - development: ['gateway.sandbox.push.apple.com', 2195], # deprecated - sandbox: ['gateway.sandbox.push.apple.com', 2195] - } - - batch_deliveries true - dispatcher :apns_tcp, host: proc { |app| HOSTS[app.environment.to_sym] } - loops Rpush::Daemon::Apns::FeedbackReceiver, if: -> { Rpush.config.apns.feedback_receiver.enabled && !Rpush.config.push } - end - end -end diff --git a/lib/rpush/daemon/apns/delivery.rb b/lib/rpush/daemon/apns/delivery.rb deleted file mode 100644 index 08945207c..000000000 --- a/lib/rpush/daemon/apns/delivery.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Rpush - module Daemon - module Apns - class Delivery < Rpush::Daemon::Delivery - def initialize(app, connection, batch) - @app = app - @connection = connection - @batch = batch - end - - def perform - @connection.write(batch_to_binary) - mark_batch_delivered - describe_deliveries - rescue Rpush::Daemon::TcpConnectionError => error - mark_batch_retryable(Time.now + 10.seconds, error) - raise - rescue StandardError => error - mark_batch_failed(error) - raise - ensure - @batch.all_processed - end - - protected - - def batch_to_binary - payload = "" - @batch.each_notification do |notification| - payload << notification.to_binary - end - payload - end - - def describe_deliveries - @batch.each_notification do |notification| - log_info("#{notification.id} sent to #{notification.device_token}") - end - end - end - end - end -end diff --git a/lib/rpush/daemon/apns/feedback_receiver.rb b/lib/rpush/daemon/apns/feedback_receiver.rb deleted file mode 100644 index e69ab4b7f..000000000 --- a/lib/rpush/daemon/apns/feedback_receiver.rb +++ /dev/null @@ -1,91 +0,0 @@ -# encoding: UTF-8 - -module Rpush - module Daemon - module Apns - class FeedbackReceiver - include Reflectable - include Loggable - - TUPLE_BYTES = 38 - HOSTS = { - production: ['feedback.push.apple.com', 2196], - development: ['feedback.sandbox.push.apple.com', 2196], # deprecated - sandbox: ['feedback.sandbox.push.apple.com', 2196] - } - - def initialize(app) - @app = app - @host, @port = HOSTS[@app.environment.to_sym] - @certificate = app.certificate - @password = app.password - @interruptible_sleep = InterruptibleSleep.new - end - - def start - return if Rpush.config.push - return unless @app.feedback_enabled - Rpush.logger.info("[#{@app.name}] Starting feedback receiver... ", true) - - @thread = Thread.new do - loop do - break if @stop - check_for_feedback - @interruptible_sleep.sleep(Rpush.config.apns.feedback_receiver.frequency) - end - - Rpush::Daemon.store.release_connection - end - - puts Rainbow('✔').green if Rpush.config.foreground && Rpush.config.foreground_logging - end - - def stop - @stop = true - @interruptible_sleep.stop - @thread.join if @thread - rescue StandardError => e - log_error(e) - reflect(:error, e) - ensure - @thread = nil - end - - def check_for_feedback - connection = nil - begin - connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port) - connection.connect - tuple = connection.read(TUPLE_BYTES) - - while tuple - timestamp, device_token = parse_tuple(tuple) - create_feedback(timestamp, device_token) - tuple = connection.read(TUPLE_BYTES) - end - rescue StandardError => e - log_error(e) - reflect(:error, e) - ensure - connection.close if connection - end - end - - protected - - def parse_tuple(tuple) - failed_at, _, device_token = tuple.unpack("N1n1H*") - [Time.at(failed_at).utc, device_token] - end - - def create_feedback(failed_at, device_token) - formatted_failed_at = failed_at.strftime('%Y-%m-%d %H:%M:%S UTC') - log_info("[FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.") - - feedback = Rpush::Daemon.store.create_apns_feedback(failed_at, device_token, @app) - reflect(:apns_feedback, feedback) - end - end - end - end -end diff --git a/lib/rpush/daemon/apns2/delivery.rb b/lib/rpush/daemon/apns2/delivery.rb index 88461a216..9e737103e 100644 --- a/lib/rpush/daemon/apns2/delivery.rb +++ b/lib/rpush/daemon/apns2/delivery.rb @@ -169,7 +169,6 @@ def prepare_headers(notification) headers['apns-expiration'] = '0' headers['apns-priority'] = '10' headers['apns-topic'] = @app.bundle_id - headers['apns-push-type'] = 'background' if notification.content_available? headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {} end diff --git a/lib/rpush/daemon/apnsp8/delivery.rb b/lib/rpush/daemon/apnsp8/delivery.rb index 1c764d091..10f8a304b 100644 --- a/lib/rpush/daemon/apnsp8/delivery.rb +++ b/lib/rpush/daemon/apnsp8/delivery.rb @@ -185,7 +185,6 @@ def prepare_headers(notification) headers['apns-priority'] = '10' headers['apns-topic'] = @app.bundle_id headers['authorization'] = "bearer #{jwt_token}" - headers['apns-push-type'] = 'background' if notification.content_available? headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {} end diff --git a/lib/rpush/daemon/dispatcher/apns_tcp.rb b/lib/rpush/daemon/dispatcher/apns_tcp.rb deleted file mode 100644 index 24d09817a..000000000 --- a/lib/rpush/daemon/dispatcher/apns_tcp.rb +++ /dev/null @@ -1,152 +0,0 @@ -module Rpush - module Daemon - module Dispatcher - class ApnsTcp < Rpush::Daemon::Dispatcher::Tcp - include Loggable - include Reflectable - - SELECT_TIMEOUT = 10 - ERROR_TUPLE_BYTES = 6 - APNS_ERRORS = { - 1 => 'Processing error', - 2 => 'Missing device token', - 3 => 'Missing topic', - 4 => 'Missing payload', - 5 => 'Missing token size', - 6 => 'Missing topic size', - 7 => 'Missing payload size', - 8 => 'Invalid device token', - 10 => 'APNs closed connection (possible maintenance)', - 255 => 'None (unknown error)' - } - - def initialize(*args) - super - @dispatch_mutex = Mutex.new - @stop_error_receiver = false - @connection.on_connect { start_error_receiver } - end - - def dispatch(payload) - @dispatch_mutex.synchronize do - @delivery_class.new(@app, @connection, payload.batch).perform - record_batch(payload.batch) - end - end - - def cleanup - if Rpush.config.push - # In push mode only a single batch is sent, followed by immediate shutdown. - # Allow the error receiver time to handle any errors. - @reconnect_disabled = true - sleep 1 - end - - @stop_error_receiver = true - super - @error_receiver_thread.join if @error_receiver_thread - rescue StandardError => e - log_error(e) - reflect(:error, e) - ensure - @error_receiver_thread = nil - end - - private - - def start_error_receiver - @error_receiver_thread = Thread.new do - check_for_error until @stop_error_receiver - Rpush::Daemon.store.release_connection - end - end - - def delivered_buffer - @delivered_buffer ||= RingBuffer.new(Rpush.config.batch_size * 10) - end - - def record_batch(batch) - batch.each_delivered do |notification| - delivered_buffer << notification.id - end - end - - def check_for_error - begin - # On Linux, select returns nil from a dropped connection. - # On OS X, Errno::EBADF is raised following a Errno::EADDRNOTAVAIL from the write call. - return unless @connection.select(SELECT_TIMEOUT) - tuple = @connection.read(ERROR_TUPLE_BYTES) - rescue *TcpConnection::TCP_ERRORS - reconnect unless @stop_error_receiver - return - end - - @dispatch_mutex.synchronize { handle_error_response(tuple) } - rescue StandardError => e - log_error(e) - end - - def handle_error_response(tuple) - if tuple - _, code, notification_id = tuple.unpack('ccN') - handle_error(code, notification_id) - else - handle_disconnect - end - - if Rpush.config.push - # Only attempt to handle a single error in Push mode. - @stop_error_receiver = true - return - end - - reconnect - ensure - delivered_buffer.clear - end - - def reconnect - return if @reconnect_disabled - log_error("Lost connection to #{@connection.host}:#{@connection.port}, reconnecting...") - @connection.reconnect_with_rescue - end - - def handle_disconnect - log_error("The APNs disconnected before any notifications could be delivered. This usually indicates you are using an invalid certificate.") if delivered_buffer.size == 0 - end - - def handle_error(code, notification_id) - notification_id = Rpush::Daemon.store.translate_integer_notification_id(notification_id) - failed_pos = delivered_buffer.index(notification_id) - description = description_for_code(code) - log_error("Notification #{notification_id} failed with error: " + description) - Rpush::Daemon.store.mark_ids_failed([notification_id], code, description, Time.now) - reflect(:notification_id_failed, @app, notification_id, code, description) - - if failed_pos - retry_ids = delivered_buffer[(failed_pos + 1)..-1] - retry_notification_ids(retry_ids, notification_id) - elsif delivered_buffer.size > 0 - log_error("Delivery sequence unknown for notifications following #{notification_id}.") - end - end - - def description_for_code(code) - APNS_ERRORS[code.to_i] ? "#{APNS_ERRORS[code.to_i]} (#{code})" : "Unknown error code #{code.inspect}. Possible Rpush bug?" - end - - def retry_notification_ids(ids, notification_id) - return if ids.size == 0 - - now = Time.now - Rpush::Daemon.store.mark_ids_retryable(ids, now) - notifications_str = 'Notification' - notifications_str += 's' if ids.size > 1 - log_warn("#{notifications_str} #{ids.join(', ')} will be retried due to the failure of notification #{notification_id}.") - ids.each { |id| reflect(:notification_id_will_retry, @app, id, now) } - end - end - end - end -end diff --git a/lib/rpush/daemon/dispatcher/tcp.rb b/lib/rpush/daemon/dispatcher/tcp.rb deleted file mode 100644 index a043ea08f..000000000 --- a/lib/rpush/daemon/dispatcher/tcp.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Rpush - module Daemon - module Dispatcher - class Tcp - def initialize(app, delivery_class, options = {}) - @app = app - @delivery_class = delivery_class - @host, @port = options[:host].call(@app) - @connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port) - end - - def dispatch(payload) - @delivery_class.new(@app, @connection, payload.notification, payload.batch).perform - end - - def cleanup - @connection.close if @connection - end - end - end - end -end diff --git a/lib/rpush/daemon/gcm.rb b/lib/rpush/daemon/gcm.rb deleted file mode 100644 index 8b1620c38..000000000 --- a/lib/rpush/daemon/gcm.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Rpush - module Daemon - module Gcm - extend ServiceConfigMethods - - dispatcher :http - end - end -end diff --git a/lib/rpush/daemon/gcm/delivery.rb b/lib/rpush/daemon/gcm/delivery.rb deleted file mode 100644 index 56c660b9b..000000000 --- a/lib/rpush/daemon/gcm/delivery.rb +++ /dev/null @@ -1,241 +0,0 @@ -module Rpush - module Daemon - module Gcm - # https://firebase.google.com/docs/cloud-messaging/server - class Delivery < Rpush::Daemon::Delivery - include MultiJsonHelper - - host = 'https://fcm.googleapis.com' - FCM_URI = URI.parse("#{host}/fcm/send") - UNAVAILABLE_STATES = %w(Unavailable BadGateway InternalServerError) - INVALID_REGISTRATION_ID_STATES = %w(InvalidRegistration MismatchSenderId NotRegistered InvalidPackageName) - - def initialize(app, http, notification, batch) - @app = app - @http = http - @notification = notification - @batch = batch - end - - def perform - handle_response(do_post) - rescue SocketError => error - mark_retryable(@notification, Time.now + 10.seconds, error) - raise - rescue StandardError => error - mark_failed(error) - raise - ensure - @batch.notification_processed - end - - protected - - def handle_response(response) - case response.code.to_i - when 200 - ok(response) - when 400 - bad_request - when 401 - unauthorized - when 500 - internal_server_error(response) - when 502 - bad_gateway(response) - when 503 - service_unavailable(response) - when 500..599 - other_5xx_error(response) - else - fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i]) - end - end - - def ok(response) - results = process_response(response) - handle_successes(results.successes) - - if results.failures.any? - handle_failures(results.failures, response) - else - mark_delivered - log_info("#{@notification.id} sent to #{@notification.registration_ids.join(', ')}") - end - end - - def process_response(response) - body = multi_json_load(response.body) - results = Results.new(body['results'], @notification.registration_ids) - results.process(invalid: INVALID_REGISTRATION_ID_STATES, unavailable: UNAVAILABLE_STATES) - results - end - - def handle_successes(successes) - successes.each do |result| - reflect(:gcm_delivered_to_recipient, @notification, result[:registration_id]) - next unless result.key?(:canonical_id) - reflect(:gcm_canonical_id, result[:registration_id], result[:canonical_id]) - end - end - - def handle_failures(failures, response) - if failures[:unavailable].count == @notification.registration_ids.count - retry_delivery(@notification, response) - log_warn("All recipients unavailable. #{retry_message}") - else - if failures[:unavailable].any? - unavailable_idxs = failures[:unavailable].map { |result| result[:index] } - new_notification = create_new_notification(response, unavailable_idxs) - failures.description += " #{unavailable_idxs.join(', ')} will be retried as notification #{new_notification.id}." - end - handle_errors(failures) - fail Rpush::DeliveryError.new(nil, @notification.id, failures.description) - end - end - - def handle_errors(failures) - failures.each do |result| - reflect(:gcm_failed_to_recipient, @notification, result[:error], result[:registration_id]) - end - failures[:invalid].each do |result| - reflect(:gcm_invalid_registration_id, @app, result[:error], result[:registration_id]) - end - end - - def create_new_notification(response, unavailable_idxs) - attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle } - registration_ids = @notification.registration_ids.values_at(*unavailable_idxs) - Rpush::Daemon.store.create_gcm_notification(attrs, @notification.data, - registration_ids, deliver_after_header(response), @app) - end - - def bad_request - fail Rpush::DeliveryError.new(400, @notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.') - end - - def unauthorized - fail Rpush::DeliveryError.new(401, @notification.id, 'Unauthorized, check your App auth_key.') - end - - def internal_server_error(response) - retry_delivery(@notification, response) - log_warn("GCM responded with an Internal Error. " + retry_message) - end - - def bad_gateway(response) - retry_delivery(@notification, response) - log_warn("GCM responded with a Bad Gateway Error. " + retry_message) - end - - def service_unavailable(response) - retry_delivery(@notification, response) - log_warn("GCM responded with an Service Unavailable Error. " + retry_message) - end - - def other_5xx_error(response) - retry_delivery(@notification, response) - log_warn("GCM responded with a 5xx Error. " + retry_message) - end - - def deliver_after_header(response) - Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after']) - end - - def retry_delivery(notification, response) - time = deliver_after_header(response) - if time - mark_retryable(notification, time) - else - mark_retryable_exponential(notification) - end - end - - def retry_message - "Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} (retry #{@notification.retries})." - end - - def do_post - post = Net::HTTP::Post.new(FCM_URI.path, 'Content-Type' => 'application/json', - 'Authorization' => "key=#{@app.auth_key}") - post.body = @notification.as_json.to_json - @http.request(FCM_URI, post) - end - end - - class Results - attr_reader :successes, :failures - - def initialize(results_data, registration_ids) - @results_data = results_data - @registration_ids = registration_ids - end - - def process(failure_partitions = {}) # rubocop:disable Metrics/AbcSize - @successes = [] - @failures = Failures.new - failure_partitions.each_key do |category| - failures[category] = [] - end - - @results_data.each_with_index do |result, index| - entry = { - registration_id: @registration_ids[index], - index: index - } - if result['message_id'] - entry[:canonical_id] = result['registration_id'] if result['registration_id'].present? - successes << entry - elsif result['error'] - entry[:error] = result['error'] - failures << entry - failure_partitions.each do |category, error_states| - failures[category] << entry if error_states.include?(result['error']) - end - end - end - failures.all_failed = failures.count == @registration_ids.count - end - end - - class Failures < Hash - include Enumerable - attr_writer :all_failed, :description - - def initialize - super[:all] = [] - end - - def each - self[:all].each { |x| yield x } - end - - def <<(item) - self[:all] << item - end - - def description - @description ||= describe - end - - def any? - self[:all].any? - end - - private - - def describe - if @all_failed - error_description = "Failed to deliver to all recipients." - else - index_list = map { |item| item[:index] } - error_description = "Failed to deliver to recipients #{index_list.join(', ')}." - end - - error_list = map { |item| item[:error] } - error_description + " Errors: #{error_list.join(', ')}." - end - end - end - end -end \ No newline at end of file diff --git a/lib/rpush/daemon/service_config_methods.rb b/lib/rpush/daemon/service_config_methods.rb index 5dc7393f1..fed762ac9 100644 --- a/lib/rpush/daemon/service_config_methods.rb +++ b/lib/rpush/daemon/service_config_methods.rb @@ -3,8 +3,6 @@ module Daemon module ServiceConfigMethods DISPATCHERS = { http: Rpush::Daemon::Dispatcher::Http, - tcp: Rpush::Daemon::Dispatcher::Tcp, - apns_tcp: Rpush::Daemon::Dispatcher::ApnsTcp, apns_http2: Rpush::Daemon::Dispatcher::ApnsHttp2, apnsp8_http2: Rpush::Daemon::Dispatcher::Apnsp8Http2 } diff --git a/lib/rpush/daemon/store/active_record.rb b/lib/rpush/daemon/store/active_record.rb index 6e1d4fbdf..f90c7b935 100644 --- a/lib/rpush/daemon/store/active_record.rb +++ b/lib/rpush/daemon/store/active_record.rb @@ -138,26 +138,14 @@ def mark_ids_failed(ids, code, description, time) end end - def create_apns_feedback(failed_at, device_token, app) - with_database_reconnect_and_retry do - Rpush::Client::ActiveRecord::Apns::Feedback.create!(failed_at: failed_at, - device_token: device_token, app_id: app.id) - end - end - def create_fcm_notification(attrs, data, app) notification = Rpush::Client::ActiveRecord::Fcm::Notification.new create_fcm_like_notification(notification, attrs, data, app) end - def create_gcm_notification(attrs, data, registration_ids, deliver_after, app) - notification = Rpush::Client::ActiveRecord::Gcm::Notification.new - create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) - end - def create_adm_notification(attrs, data, registration_ids, deliver_after, app) notification = Rpush::Client::ActiveRecord::Adm::Notification.new - create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) + create_adm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) end def update_app(app) @@ -209,7 +197,7 @@ def create_fcm_like_notification(notification, attrs, data, app) # rubocop:disab end end - def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable Metrics/ParameterLists + def create_adm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable Metrics/ParameterLists with_database_reconnect_and_retry do notification.assign_attributes(attrs) notification.data = data diff --git a/lib/rpush/daemon/store/interface.rb b/lib/rpush/daemon/store/interface.rb index 515732fd2..94c9629dd 100644 --- a/lib/rpush/daemon/store/interface.rb +++ b/lib/rpush/daemon/store/interface.rb @@ -4,8 +4,8 @@ module Store class Interface PUBLIC_METHODS = [:deliverable_notifications, :mark_retryable, :mark_batch_retryable, :mark_delivered, :mark_batch_delivered, - :mark_failed, :mark_batch_failed, :create_apns_feedback, - :create_fcm_notification, :create_gcm_notification, :create_adm_notification, + :mark_failed, :mark_batch_failed, + :create_fcm_notification, :create_adm_notification, :update_app, :update_notification, :release_connection, :all_apps, :app, :mark_ids_failed, :mark_ids_retryable, :reopen_log, :pending_delivery_count, :translate_integer_notification_id] diff --git a/lib/rpush/daemon/store/redis.rb b/lib/rpush/daemon/store/redis.rb index 54808e8cd..278ceb44a 100644 --- a/lib/rpush/daemon/store/redis.rb +++ b/lib/rpush/daemon/store/redis.rb @@ -88,23 +88,14 @@ def mark_ids_retryable(ids, deliver_after) end end - def create_apns_feedback(failed_at, device_token, app) - Rpush::Client::Redis::Apns::Feedback.create!(failed_at: failed_at, device_token: device_token, app_id: app.id) - end - def create_fcm_notification(attrs, data, app) notification = Rpush::Client::Redis::Fcm::Notification.new create_fcm_like_notification(notification, attrs, data, app) end - def create_gcm_notification(attrs, data, registration_ids, deliver_after, app) - notification = Rpush::Client::Redis::Gcm::Notification.new - create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) - end - def create_adm_notification(attrs, data, registration_ids, deliver_after, app) notification = Rpush::Client::Redis::Adm::Notification.new - create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) + create_adm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) end def update_app(app) @@ -151,7 +142,7 @@ def create_fcm_like_notification(notification, attrs, data, app) # rubocop:disab notification end - def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable Metrics/ParameterLists + def create_adm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable Metrics/ParameterLists notification.assign_attributes(attrs) notification.data = data notification.registration_ids = registration_ids diff --git a/lib/rpush/daemon/tcp_connection.rb b/lib/rpush/daemon/tcp_connection.rb deleted file mode 100644 index 381791d38..000000000 --- a/lib/rpush/daemon/tcp_connection.rb +++ /dev/null @@ -1,190 +0,0 @@ -module Rpush - module Daemon - class TcpConnectionError < StandardError; end - - class TcpConnection - include Reflectable - include Loggable - - OSX_TCP_KEEPALIVE = 0x10 # Defined in - KEEPALIVE_INTERVAL = 5 - KEEPALIVE_IDLE = 5 - KEEPALIVE_MAX_FAIL_PROBES = 1 - TCP_ERRORS = [SystemCallError, OpenSSL::OpenSSLError, IOError] - - attr_accessor :last_touch - attr_reader :host, :port - - def self.idle_period - 30.minutes - end - - def initialize(app, host, port) - @app = app - @host = host - @port = port - @certificate = app.certificate - @password = app.password - @connected = false - @connection_callbacks = [] - touch - end - - def on_connect(&blk) - raise 'already connected' if @connected - @connection_callbacks << blk - end - - def connect - @ssl_context = setup_ssl_context - @tcp_socket, @ssl_socket = connect_socket - @connected = true - - @connection_callbacks.each do |blk| - begin - blk.call - rescue StandardError => e - log_error(e) - end - end - - @connection_callbacks.clear - end - - def close - @ssl_socket.close if @ssl_socket - @tcp_socket.close if @tcp_socket - rescue IOError # rubocop:disable HandleExceptions - end - - def read(num_bytes) - @ssl_socket.read(num_bytes) if @ssl_socket - end - - def select(timeout) - IO.select([@ssl_socket], nil, nil, timeout) if @ssl_socket - end - - def write(data) - connect unless @connected - reconnect_idle if idle_period_exceeded? - - retry_count = 0 - - begin - write_data(data) - rescue *TCP_ERRORS => e - retry_count += 1 - - if retry_count == 1 - log_error("Lost connection to #{@host}:#{@port} (#{e.class.name}, #{e.message}), reconnecting...") - reflect(:tcp_connection_lost, @app, e) - end - - if retry_count <= 3 - reconnect_with_rescue - sleep 1 - retry - else - raise TcpConnectionError, "#{@app.name} tried #{retry_count - 1} times to reconnect but failed (#{e.class.name}, #{e.message})." - end - end - end - - def reconnect_with_rescue - reconnect - rescue StandardError => e - log_error(e) - end - - def reconnect - close - @tcp_socket, @ssl_socket = connect_socket - end - - protected - - def reconnect_idle - log_info("Idle period exceeded, reconnecting...") - reconnect - end - - def idle_period_exceeded? - Time.now - last_touch > self.class.idle_period - end - - def write_data(data) - @ssl_socket.write(data) - @ssl_socket.flush - touch - end - - def touch - self.last_touch = Time.now - end - - def setup_ssl_context - ssl_context = OpenSSL::SSL::SSLContext.new - ssl_context.key = OpenSSL::PKey::RSA.new(@certificate, @password) - ssl_context.cert = OpenSSL::X509::Certificate.new(@certificate) - ssl_context - end - - def connect_socket - touch - check_certificate_expiration - - tcp_socket = TCPSocket.new(@host, @port) - tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) - tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true) - - # Linux - if [:SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all? { |c| Socket.const_defined?(c) } - tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, KEEPALIVE_IDLE) - tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, KEEPALIVE_INTERVAL) - tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, KEEPALIVE_MAX_FAIL_PROBES) - end - - # OSX - if RUBY_PLATFORM =~ /darwin/ - tcp_socket.setsockopt(Socket::IPPROTO_TCP, OSX_TCP_KEEPALIVE, KEEPALIVE_IDLE) - end - - ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context) - ssl_socket.sync = true - ssl_socket.connect - [tcp_socket, ssl_socket] - rescue *TCP_ERRORS => error - if error.message =~ /certificate revoked/i - log_error('Certificate has been revoked.') - reflect(:ssl_certificate_revoked, @app, error) - end - raise TcpConnectionError, "#{error.class.name}, #{error.message}" - end - - def check_certificate_expiration - cert = @ssl_context.cert - if certificate_expired? - log_error(certificate_msg('expired')) - raise Rpush::CertificateExpiredError.new(@app, cert.not_after) - elsif certificate_expires_soon? - log_warn(certificate_msg('will expire')) - reflect(:ssl_certificate_will_expire, @app, cert.not_after) - end - end - - def certificate_msg(msg) - time = @ssl_context.cert.not_after.utc.strftime('%Y-%m-%d %H:%M:%S UTC') - "Certificate #{msg} at #{time}." - end - - def certificate_expired? - @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < Time.now.utc - end - - def certificate_expires_soon? - @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < (Time.now + 1.month).utc - end - end - end -end diff --git a/lib/rpush/reflection_collection.rb b/lib/rpush/reflection_collection.rb index f2574870c..c87c8c851 100644 --- a/lib/rpush/reflection_collection.rb +++ b/lib/rpush/reflection_collection.rb @@ -5,10 +5,9 @@ class NoSuchReflectionError < StandardError; end REFLECTIONS = [ :apns_feedback, :notification_enqueued, :notification_delivered, :notification_failed, :notification_will_retry, - :gcm_delivered_to_recipient, :gcm_failed_to_recipient, :gcm_canonical_id, :gcm_invalid_registration_id, :fcm_delivered_to_recipient, :fcm_failed_to_recipient, :fcm_canonical_id, :fcm_invalid_device_token, :error, :adm_canonical_id, :adm_failed_to_recipient, :wns_invalid_channel, - :tcp_connection_lost, :ssl_certificate_will_expire, :ssl_certificate_revoked, + :ssl_certificate_will_expire, :ssl_certificate_revoked, :notification_id_will_retry, :notification_id_failed ] diff --git a/lib/rpush/version.rb b/lib/rpush/version.rb index 992422280..8ead7e7f4 100644 --- a/lib/rpush/version.rb +++ b/lib/rpush/version.rb @@ -1,7 +1,7 @@ module Rpush module VERSION - MAJOR = 7 - MINOR = 1 + MAJOR = 9 + MINOR = 2 TINY = 0 PRE = nil diff --git a/rpush.gemspec b/rpush.gemspec index cdd42448a..fdc736498 100644 --- a/rpush.gemspec +++ b/rpush.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin`.split("\n").map { |f| File.basename(f) } s.require_paths = ["lib"] - s.required_ruby_version = '>= 2.7.0' + s.required_ruby_version = '>= 3.0.0' s.post_install_message = <<~POST_INSTALL_MESSAGE When upgrading Rpush, don't forget to run `bundle exec rpush init` to get all the latest migrations. @@ -36,7 +36,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'net-http-persistent' s.add_runtime_dependency 'net-http2', '~> 0.18', '>= 0.18.3' s.add_runtime_dependency 'jwt', '>= 1.5.6' - s.add_runtime_dependency 'activesupport', '>= 5.1' + s.add_runtime_dependency 'activesupport', '>= 6.0', '!= 7.1.4', '!= 7.2.0', '!= 7.2.1' # https://github.com/rails/rails/issues/52820 s.add_runtime_dependency 'thor', ['>= 0.18.1', '< 2.0'] s.add_runtime_dependency 'railties' s.add_runtime_dependency 'rainbow' @@ -55,11 +55,14 @@ Gem::Specification.new do |s| s.add_development_dependency 'appraisal' s.add_development_dependency 'codeclimate-test-reporter', '1.0.7' s.add_development_dependency 'simplecov' - s.add_development_dependency 'rubocop', '~> 1.12.0' + s.add_development_dependency 'rubocop', '~> 1.66' s.add_development_dependency 'rubocop-performance' + s.add_development_dependency 'rubocop-rake' + s.add_development_dependency 'rubocop-rspec' + s.add_development_dependency 'rubocop-rails' s.add_development_dependency 'byebug' s.add_development_dependency 'pg' s.add_development_dependency 'mysql2' - s.add_development_dependency 'sqlite3' + s.add_development_dependency 'sqlite3', '~> 1.4' end diff --git a/spec/functional/apns2_spec.rb b/spec/functional/apns2_spec.rb index 3415a685e..92890b9b4 100644 --- a/spec/functional/apns2_spec.rb +++ b/spec/functional/apns2_spec.rb @@ -79,8 +79,7 @@ def create_notification headers: { 'apns-expiration' => '0', 'apns-priority' => '10', - 'apns-topic' => 'com.example.app', - 'apns-push-type' => 'background' + 'apns-topic' => 'com.example.app' } } ) @@ -114,8 +113,7 @@ def create_notification headers: { 'apns-topic' => bundle_id, 'apns-expiration' => '0', - 'apns-priority' => '10', - 'apns-push-type' => 'background' + 'apns-priority' => '10' } } ).and_return(fake_http2_request) diff --git a/spec/functional/apns_spec.rb b/spec/functional/apns_spec.rb deleted file mode 100644 index 9cc7f1354..000000000 --- a/spec/functional/apns_spec.rb +++ /dev/null @@ -1,162 +0,0 @@ -require 'functional_spec_helper' - -describe 'APNs' do - let(:app) { create_app } - let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) } - let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) } - let(:io_double) { double(select: nil) } - let(:delivered_ids) { [] } - let(:failed_ids) { [] } - let(:retry_ids) { [] } - - before do - Rpush.config.push_poll = 0.5 - stub_tcp_connection(tcp_socket, ssl_socket, io_double) - end - - def create_app - app = Rpush::Apns::App.new - app.certificate = TEST_CERT - app.name = 'test' - app.environment = 'sandbox' - app.save! - app - end - - def create_notification - notification = Rpush::Apns::Notification.new - notification.app = app - notification.alert = 'test' - notification.device_token = 'a' * 108 - notification.save! - notification - end - - def wait - sleep 0.1 - end - - def wait_for_notification_to_deliver(notification) - timeout { wait until delivered_ids.include?(notification.id) } - end - - def wait_for_notification_to_fail(notification) - timeout { wait until failed_ids.include?(notification.id) } - end - - def wait_for_notification_to_retry(notification) - timeout { wait until retry_ids.include?(notification.id) } - end - - def fail_notification(notification) - allow(ssl_socket).to receive_messages(read: [8, 4, notification.id].pack('ccN')) - enable_io_select - end - - def enable_io_select - called = false - allow(io_double).to receive(:select) do - if called - nil - else - called = true - end - end - end - - it 'delivers a notification successfully' do - notification = create_notification - expect do - Rpush.push - notification.reload - end.to change(notification, :delivered).to(true) - end - - it 'receives feedback' do - app - tuple = "N\xE3\x84\r\x00 \x83OxfU\xEB\x9F\x84aJ\x05\xAD}\x00\xAF1\xE5\xCF\xE9:\xC3\xEA\a\x8F\x1D\xA4M*N\xB0\xCE\x17" - allow(ssl_socket).to receive(:read).and_return(tuple, nil) - Rpush.apns_feedback - feedback = Rpush::Apns::Feedback.all.first - expect(feedback).not_to be_nil - expect(feedback.app_id).to eq(app.id) - expect(feedback.device_token).to eq('834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17') - end - - describe 'delivery failures' do - before do - Rpush.reflect do |on| - on.notification_delivered do |n| - delivered_ids << n.id - end - - on.notification_id_failed do |_, n_id| - failed_ids << n_id - end - - on.notification_id_will_retry do |_, n_id| - retry_ids << n_id - end - - on.notification_will_retry do |n| - retry_ids << n.id - end - end - - Rpush.embed - end - - after do - Rpush.reflection_stack.clear - Rpush.reflection_stack.push(Rpush::ReflectionCollection.new) - - timeout { Rpush.shutdown } - end - - it 'fails to deliver a notification' do - notification = create_notification - wait_for_notification_to_deliver(notification) - fail_notification(notification) - wait_for_notification_to_fail(notification) - end - - describe 'with a failed connection' do - it 'retries all notifications' do - allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(sleep: nil) - expect(ssl_socket).to receive(:write).at_least(1).times.and_raise(Errno::EPIPE) - notifications = 2.times.map { create_notification } - notifications.each { |n| wait_for_notification_to_retry(n) } - end - end - - describe 'with multiple notifications' do - let(:notification1) { create_notification } - let(:notification2) { create_notification } - let(:notification3) { create_notification } - let(:notification4) { create_notification } - let(:notifications) { [notification1, notification2, notification3, notification4] } - - it 'marks the correct notification as failed' do - notifications.each { |n| wait_for_notification_to_deliver(n) } - fail_notification(notification2) - wait_for_notification_to_fail(notification2) - end - - it 'does not mark prior notifications as failed' do - notifications.each { |n| wait_for_notification_to_deliver(n) } - fail_notification(notification2) - wait_for_notification_to_fail(notification2) - - expect(failed_ids).to_not include(notification1.id) - notification1.reload - expect(notification1.delivered).to eq(true) - end - - it 'marks notifications following the failed one as retryable' do - notifications.each { |n| wait_for_notification_to_deliver(n) } - fail_notification(notification2) - [notification3, notification4].each { |n| wait_for_notification_to_retry(n) } - end - end - end -end diff --git a/spec/functional/cli_spec.rb b/spec/functional/cli_spec.rb index e02fef4f2..30b921086 100644 --- a/spec/functional/cli_spec.rb +++ b/spec/functional/cli_spec.rb @@ -2,31 +2,57 @@ describe Rpush::CLI do def create_app - app = Rpush::Apns::App.new + app = Rpush::Apns2::App.new app.certificate = TEST_CERT app.name = 'test' - app.environment = 'sandbox' + app.environment = 'development' + app.bundle_id = 'com.example.app' app.save! app end - describe 'status' do - let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) } - let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) } - let(:io_double) { double(select: nil) } + let(:fake_client) { + double( + prepare_request: fake_http2_request, + close: 'ok', + call_async: 'ok', + join: 'ok', + on: 'ok' + ) + } + let(:fake_http2_request) { double } + let(:fake_http_resp_headers) { + { + ":status" => "200", + "apns-id"=>"C6D65840-5E3F-785A-4D91-B97D305C12F6" + } + } + let(:fake_http_resp_body) { '' } - before do - create_app - stub_tcp_connection(tcp_socket, ssl_socket, io_double) - Rpush.embed + before do + create_app + Rpush.config.push_poll = 0.5 - timeout do - Thread.pass until File.exist?(Rpush::Daemon::Rpc.socket_path) - end - end + allow(NetHttp2::Client). + to receive(:new).and_return(fake_client) + allow(fake_http2_request). + to receive(:on).with(:headers). + and_yield(fake_http_resp_headers) + allow(fake_http2_request). + to receive(:on).with(:body_chunk). + and_yield(fake_http_resp_body) + allow(fake_http2_request). + to receive(:on).with(:close). + and_yield + + Rpush.embed + end - after { timeout { Rpush.shutdown } } + after do + timeout { Rpush.shutdown } + end + describe 'status' do it 'prints the status' do expect(subject).to receive(:configure_rpush) { true } expect(subject).to receive(:puts).with(/app_runners:/) diff --git a/spec/functional/embed_spec.rb b/spec/functional/embed_spec.rb index 0adf57096..4005ca0a8 100644 --- a/spec/functional/embed_spec.rb +++ b/spec/functional/embed_spec.rb @@ -1,49 +1,80 @@ require 'functional_spec_helper' describe 'embedding' do - let(:timeout) { 10 } - let(:app) { Rpush::Apns::App.new } - let(:notification) { Rpush::Apns::Notification.new } - let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) } - let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) } - let(:io_double) { double(select: nil) } - - before do + def create_app + app = Rpush::Apns2::App.new app.certificate = TEST_CERT app.name = 'test' - app.environment = 'sandbox' + app.environment = 'development' + app.bundle_id = 'com.example.app' app.save! + app + end + let(:fake_device_token) { 'a' * 108 } + let(:notification_data) { nil } + + def create_notification(app) + notification = Rpush::Apns2::Notification.new notification.app = app + notification.sound = 'default' notification.alert = 'test' - notification.device_token = 'a' * 108 + notification.device_token = fake_device_token + notification.data = notification_data + notification.content_available = 1 notification.save! - - stub_tcp_connection + notification end - def stub_tcp_connection - allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(connect_socket: [tcp_socket, ssl_socket]) - allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(setup_ssl_context: double.as_null_object) - stub_const('Rpush::Daemon::TcpConnection::IO', io_double) - allow(Rpush::Daemon::Apns::FeedbackReceiver).to receive_messages(new: double.as_null_object) - end + let(:app) { create_app } + let(:notification) { create_notification(app) } + + let(:fake_client) { + double( + prepare_request: fake_http2_request, + close: 'ok', + call_async: 'ok', + join: 'ok', + on: 'ok' + ) + } + let(:fake_http2_request) { double } + let(:fake_http_resp_headers) { + { + ":status" => "200", + "apns-id"=>"C6D65840-5E3F-785A-4D91-B97D305C12F6" + } + } + let(:fake_http_resp_body) { '' } before do - Rpush.config.push_poll = 5 + Rpush.config.push_poll = 0.5 + + allow(NetHttp2::Client). + to receive(:new).and_return(fake_client) + allow(fake_http2_request). + to receive(:on).with(:headers). + and_yield(fake_http_resp_headers) + allow(fake_http2_request). + to receive(:on).with(:body_chunk). + and_yield(fake_http_resp_body) + allow(fake_http2_request). + to receive(:on).with(:close). + and_yield + Rpush.embed end + after do + timeout { Rpush.shutdown } + end + it 'delivers a notification successfully' do expect do - Timeout.timeout(timeout) do - until notification.delivered - notification.reload - sleep 0.1 - end + until notification.delivered + notification.reload + sleep 0.1 end end.to change(notification, :delivered).to(true) end - - after { Timeout.timeout(timeout) { Rpush.shutdown } } end diff --git a/spec/functional/gcm_priority_spec.rb b/spec/functional/gcm_priority_spec.rb deleted file mode 100644 index 33992d533..000000000 --- a/spec/functional/gcm_priority_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'functional_spec_helper' - -describe 'GCM priority' do - let(:app) { Rpush::Gcm::App.new } - let(:notification) { Rpush::Gcm::Notification.new } - let(:hydrated_notification) { Rpush::Gcm::Notification.find(notification.id) } - let(:response) { double(Net::HTTPResponse, code: 200) } - let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) } - let(:priority) { 'normal' } - - before do - app.name = 'test' - app.auth_key = 'abc123' - app.save! - - notification.app_id = app.id - notification.registration_ids = ['foo'] - notification.data = { message: 'test' } - notification.priority = priority - notification.save! - - allow(Net::HTTP::Persistent).to receive_messages(new: http) - end - - it 'supports normal priority' do - expect(hydrated_notification.as_json['priority']).to eq('normal') - end - - context 'high priority' do - let(:priority) { 'high' } - - it 'supports high priority' do - expect(hydrated_notification.as_json['priority']).to eq('high') - end - end - - it 'does not add an error when receiving expected priority' do - expect(hydrated_notification.errors.messages[:priority]).to be_empty - end -end diff --git a/spec/functional/gcm_spec.rb b/spec/functional/gcm_spec.rb deleted file mode 100644 index 7a50caaab..000000000 --- a/spec/functional/gcm_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'functional_spec_helper' - -describe 'GCM' do - let(:app) { Rpush::Gcm::App.new } - let(:notification) { Rpush::Gcm::Notification.new } - let(:response) { double(Net::HTTPResponse, code: 200) } - let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) } - - before do - app.name = 'test' - app.auth_key = 'abc123' - app.save! - - notification.app_id = app.id - notification.registration_ids = ['foo'] - notification.data = { message: 'test' } - notification.save! - - allow(Net::HTTP::Persistent).to receive_messages(new: http) - end - - it 'delivers a notification successfully' do - allow(response).to receive_messages(body: JSON.dump(results: [{ message_id: notification.registration_ids.first.to_s }])) - - expect do - Rpush.push - notification.reload - end.to change(notification, :delivered).to(true) - end - - it 'fails to deliver a notification successfully' do - allow(response).to receive_messages(body: JSON.dump(results: [{ error: 'Err' }])) - Rpush.push - notification.reload - expect(notification.delivered).to eq(false) - end - - it 'retries notification that fail due to a SocketError' do - expect(http).to receive(:request).and_raise(SocketError.new) - expect(notification.deliver_after).to be_nil - expect do - Rpush.push - notification.reload - end.to change(notification, :deliver_after).to(kind_of(Time)) - end -end diff --git a/spec/functional/new_app_spec.rb b/spec/functional/new_app_spec.rb deleted file mode 100644 index a1580413c..000000000 --- a/spec/functional/new_app_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'functional_spec_helper' - -describe 'New app loading' do - let(:timeout) { 10 } - let(:app) { create_app } - let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) } - let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) } - let(:io_double) { double(select: nil) } - - before do - stub_tcp_connection - end - - def create_app - app = Rpush::Apns::App.new - app.certificate = TEST_CERT - app.name = 'test' - app.environment = 'sandbox' - app.save! - app - end - - def create_notification - notification = Rpush::Apns::Notification.new - notification.app = app - notification.alert = 'test' - notification.device_token = 'a' * 108 - notification.save! - notification - end - - def stub_tcp_connection - allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(connect_socket: [tcp_socket, ssl_socket]) - allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(setup_ssl_context: double.as_null_object) - stub_const('Rpush::Daemon::TcpConnection::IO', io_double) - end - - it 'delivers a notification successfully' do - notification = create_notification - Rpush.push - notification.reload - expect(notification.delivered).to eq(true) - end -end diff --git a/spec/functional/retry_spec.rb b/spec/functional/retry_spec.rb index 6a1ad038e..d1eeac846 100644 --- a/spec/functional/retry_spec.rb +++ b/spec/functional/retry_spec.rb @@ -1,10 +1,12 @@ require 'functional_spec_helper' describe 'Retries' do - let(:app) { Rpush::Gcm::App.new } - let(:notification) { Rpush::Gcm::Notification.new } + let(:app) { Rpush::Fcm::App.new } + let(:notification) { Rpush::Fcm::Notification.new } let(:response) { double(Net::HTTPResponse, code: 200) } let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) } + let(:fake_device_token) { 'a' * 108 } + let(:creds) {double(Google::Auth::UserRefreshCredentials)} before do Rpush::Daemon.common_init @@ -14,7 +16,7 @@ app.save! notification.app_id = app.id - notification.registration_ids = ['foo'] + notification.device_token = 'foo' notification.data = { message: 'test' } notification.save! @@ -23,7 +25,22 @@ end allow(Net::HTTP::Persistent).to receive_messages(new: http) - allow(response).to receive_messages(body: JSON.dump(results: [{ message_id: notification.registration_ids.first.to_s }])) + allow(creds).to receive(:fetch_access_token).and_return({'access_token': 'face_access_token'}) + + allow(::Google::Auth::ServiceAccountCredentials).to receive(:fetch_access_token).and_return({access_token: 'bbbbbb'}) + allow(::Google::Auth::ServiceAccountCredentials).to receive(:make_creds).and_return(creds) + allow_any_instance_of(::Rpush::Daemon::Fcm::Delivery).to receive(:necessary_data_exists?).and_return(true) + + example_success_body = { + "multicast_id": 108, + "success": 1, + "failure": 0, + "canonical_ids": 0, + "results": [ + { "message_id": "1:08" } + ] + }.to_json + allow(response).to receive_messages(body: example_success_body) end it 'delivers a notification due to be retried' do diff --git a/spec/functional/synchronization_spec.rb b/spec/functional/synchronization_spec.rb index 193817e7f..c66904607 100644 --- a/spec/functional/synchronization_spec.rb +++ b/spec/functional/synchronization_spec.rb @@ -2,7 +2,7 @@ describe 'Synchronization' do let(:timeout) { 10 } - let(:app) { Rpush::Gcm::App.new } + let(:app) { Rpush::Fcm::App.new } def wait_for_num_dispatchers(num) Timeout.timeout(timeout) do diff --git a/spec/functional_spec_helper.rb b/spec/functional_spec_helper.rb index 46da6f137..f524e7dd7 100644 --- a/spec/functional_spec_helper.rb +++ b/spec/functional_spec_helper.rb @@ -11,12 +11,6 @@ def timeout(&blk) Timeout.timeout(10, &blk) end -def stub_tcp_connection(tcp_socket, ssl_socket, io_double) - allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(connect_socket: [tcp_socket, ssl_socket]) - allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(setup_ssl_context: double.as_null_object) - stub_const('Rpush::Daemon::TcpConnection::IO', io_double) -end - RSpec.configure do |config| config.before(:each) do Modis.with_connection do |redis| diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8911d55bf..aeac01eb5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -17,13 +17,6 @@ def client require 'timecop' require 'activerecord-jdbc-adapter' if defined? JRUBY_VERSION -require 'rpush' -require 'rpush/daemon' -require 'rpush/client/redis' -require 'rpush/client/active_record' -require 'rpush/daemon/store/active_record' -require 'rpush/daemon/store/redis' - def active_record? client == :active_record end @@ -32,6 +25,21 @@ def redis? client == :redis end +if active_record? + require 'active_record' + if ActiveRecord::Base.respond_to?(:default_column_serializer) + # New default in Rails 7.1: https://github.com/rails/rails/pull/47422 + ActiveRecord::Base.default_column_serializer = nil + end +end + +require 'rpush' +require 'rpush/daemon' +require 'rpush/client/redis' +require 'rpush/client/active_record' +require 'rpush/daemon/store/active_record' +require 'rpush/daemon/store/redis' + require 'support/active_record_setup' if active_record? RPUSH_ROOT = '/tmp/rails_root' @@ -58,6 +66,8 @@ def after_example_cleanup end Rpush.plugins.values.each(&:unload) Rpush.instance_variable_set('@plugins', {}) + Rpush.reflection_stack.clear + Rpush.reflection_stack.push(Rpush::ReflectionCollection.new) end RSpec.configure do |config| diff --git a/spec/unit/apns_feedback_spec.rb b/spec/unit/apns_feedback_spec.rb deleted file mode 100644 index 81267aef2..000000000 --- a/spec/unit/apns_feedback_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush, 'apns_feedback' do - let!(:apns_app) { Rpush::Apns::App.create!(apns_app_params) } - let(:apns_app_params) do - { - name: 'test', - environment: 'production', - certificate: TEST_CERT - } - end - let!(:gcm_app) { Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123') } - - let(:receiver) { double(check_for_feedback: nil) } - - before do - allow(Rpush::Daemon::Apns::FeedbackReceiver).to receive(:new) { receiver } - end - - it 'initializes the daemon' do - expect(Rpush::Daemon).to receive(:common_init) - Rpush.apns_feedback - end - - it 'checks feedback for each app' do - expect(Rpush::Daemon::Apns::FeedbackReceiver).to receive(:new).with(apns_app).and_return(receiver) - expect(receiver).to receive(:check_for_feedback) - Rpush.apns_feedback - end - - context 'feedback disabled' do - let(:apns_app_params) { super().merge(feedback_enabled: false) } - - it 'does not initialize feedback receiver' do - expect(Rpush::Daemon::Apns::FeedbackReceiver).not_to receive(:new) - Rpush.apns_feedback - end - end -end diff --git a/spec/unit/client/active_record/gcm/app_spec.rb b/spec/unit/client/active_record/gcm/app_spec.rb deleted file mode 100644 index f7d2925b6..000000000 --- a/spec/unit/client/active_record/gcm/app_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush::Client::ActiveRecord::Gcm::App do - it_behaves_like 'Rpush::Client::Gcm::App' - it_behaves_like 'Rpush::Client::ActiveRecord::App' -end if active_record? diff --git a/spec/unit/client/active_record/gcm/notification_spec.rb b/spec/unit/client/active_record/gcm/notification_spec.rb deleted file mode 100644 index f83a45607..000000000 --- a/spec/unit/client/active_record/gcm/notification_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush::Client::ActiveRecord::Gcm::Notification do - it_behaves_like 'Rpush::Client::Gcm::Notification' - it_behaves_like 'Rpush::Client::ActiveRecord::Notification' - - subject(:notification) { described_class.new } - let(:app) { Rpush::Gcm::App.create!(name: 'test', auth_key: 'abc') } - - it 'accepts non-booleans as a truthy value' do - notification.dry_run = 'Not a boolean' - expect(notification.as_json['dry_run']).to eq true - end -end if active_record? diff --git a/spec/unit/client/active_record/shared/app.rb b/spec/unit/client/active_record/shared/app.rb index acc1b8502..21b7a3c7f 100644 --- a/spec/unit/client/active_record/shared/app.rb +++ b/spec/unit/client/active_record/shared/app.rb @@ -8,7 +8,7 @@ app = Rpush::Apns::App.new(name: 'test', environment: 'development', certificate: TEST_CERT) expect(app.valid?).to eq(true) - app = Rpush::Gcm::App.new(name: 'test', environment: 'production', auth_key: TEST_CERT) + app = Rpush::Fcm::App.new(name: 'test', environment: 'production', json_key: TEST_CERT) expect(app.valid?).to eq(true) end end diff --git a/spec/unit/client/redis/gcm/app_spec.rb b/spec/unit/client/redis/gcm/app_spec.rb deleted file mode 100644 index 935363e9c..000000000 --- a/spec/unit/client/redis/gcm/app_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush::Client::Redis::Gcm::App do - it_behaves_like 'Rpush::Client::Gcm::App' -end if redis? diff --git a/spec/unit/client/redis/gcm/notification_spec.rb b/spec/unit/client/redis/gcm/notification_spec.rb deleted file mode 100644 index f1c163bd7..000000000 --- a/spec/unit/client/redis/gcm/notification_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush::Client::Redis::Gcm::Notification do - it_behaves_like 'Rpush::Client::Gcm::Notification' -end if redis? diff --git a/spec/unit/client/shared/apns/notification.rb b/spec/unit/client/shared/apns/notification.rb index cf8b58d26..4af9be30a 100644 --- a/spec/unit/client/shared/apns/notification.rb +++ b/spec/unit/client/shared/apns/notification.rb @@ -165,21 +165,6 @@ end end - describe 'content_available?' do - context 'if not set' do - it 'should be false' do - expect(notification.content_available?).to be_falsey - end - end - - context 'if set' do - it 'should be true' do - notification.content_available = true - expect(notification.content_available?).to be_truthy - end - end - end - describe 'url-args' do it 'includes url-args in the payload' do notification.url_args = ['url-arg-1'] diff --git a/spec/unit/client/shared/fcm/notification.rb b/spec/unit/client/shared/fcm/notification.rb index 55109f862..6d4162fee 100644 --- a/spec/unit/client/shared/fcm/notification.rb +++ b/spec/unit/client/shared/fcm/notification.rb @@ -26,7 +26,7 @@ expect(notification.as_json['message']['notification']).to eq({"title"=>"title", "body"=>"body"}) end - it "moves notification keys to the correcdt location" do + it "moves notification keys to the correct location" do notification.app = app notification.device_token = "valid" notification.notification = { "title" => "valid", "body" => "valid", "color" => "valid for android" } @@ -82,6 +82,11 @@ expect(notification.as_json['message']).to have_key 'notification' end + it 'includes the badge if defined' do + notification.badge = 3 + expect(notification.as_json['message']['apns']['payload']['aps']['badge']).to eq(3) + end + it 'excludes the notification payload if undefined' do expect(notification.as_json['message']).not_to have_key 'notification' end diff --git a/spec/unit/client/shared/gcm/app.rb b/spec/unit/client/shared/gcm/app.rb deleted file mode 100644 index f391bb689..000000000 --- a/spec/unit/client/shared/gcm/app.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'unit_spec_helper' - -shared_examples 'Rpush::Client::Gcm::App' do -end diff --git a/spec/unit/client/shared/gcm/notification.rb b/spec/unit/client/shared/gcm/notification.rb deleted file mode 100644 index 207b2a5b8..000000000 --- a/spec/unit/client/shared/gcm/notification.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'unit_spec_helper' - -shared_examples 'Rpush::Client::Gcm::Notification' do - let(:app) { Rpush::Gcm::App.create!(name: 'test', auth_key: 'abc') } - let(:notification) { described_class.new } - - it "has a 'data' payload limit of 4096 bytes" do - notification.data = { key: "a" * 4096 } - expect(notification.valid?).to be_falsey - expect(notification.errors[:base]).to eq ["Notification payload data cannot be larger than 4096 bytes."] - end - - it 'limits the number of registration ids to 1000' do - notification.registration_ids = ['a'] * (1000 + 1) - expect(notification.valid?).to be_falsey - expect(notification.errors[:base]).to eq ["Number of registration_ids cannot be larger than 1000."] - end - - it 'validates expiry is present if collapse_key is set' do - notification.collapse_key = 'test' - notification.expiry = nil - expect(notification.valid?).to be_falsey - expect(notification.errors[:expiry]).to eq ['must be set when using a collapse_key'] - end - - it 'includes time_to_live in the payload' do - notification.expiry = 100 - expect(notification.as_json['time_to_live']).to eq 100 - end - - it 'includes content_available in the payload' do - notification.content_available = true - expect(notification.as_json['content_available']).to eq true - end - - it 'includes mutable_content in the payload' do - notification.mutable_content = true - expect(notification.as_json['mutable_content']).to eq true - end - - it 'sets the priority to high when set to high' do - notification.priority = 'high' - expect(notification.as_json['priority']).to eq 'high' - end - - it 'sets the priority to normal when set to normal' do - notification.priority = 'normal' - expect(notification.as_json['priority']).to eq 'normal' - end - - it 'validates the priority is either "normal" or "high"' do - notification.priority = 'invalid' - expect(notification.errors[:priority]).to eq ['must be one of either "normal" or "high"'] - end - - it 'excludes the priority if it is not defined' do - expect(notification.as_json).not_to have_key 'priority' - end - - it 'includes the notification payload if defined' do - notification.notification = { key: 'any key is allowed' } - expect(notification.as_json).to have_key 'notification' - end - - it 'excludes the notification payload if undefined' do - expect(notification.as_json).not_to have_key 'notification' - end - - it 'includes the dry_run payload if defined' do - notification.dry_run = true - expect(notification.as_json['dry_run']).to eq true - end - - it 'excludes the dry_run payload if undefined' do - expect(notification.as_json).not_to have_key 'dry_run' - end -end diff --git a/spec/unit/daemon/apns/delivery_spec.rb b/spec/unit/daemon/apns/delivery_spec.rb deleted file mode 100644 index f6f1186fd..000000000 --- a/spec/unit/daemon/apns/delivery_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush::Daemon::Apns::Delivery do - let(:app) { double(name: 'MyApp') } - let(:notification1) { double.as_null_object } - let(:notification2) { double.as_null_object } - let(:batch) { double(mark_all_failed: nil, mark_all_delivered: nil, all_processed: nil) } - let(:logger) { double(error: nil, info: nil) } - let(:connection) { double(select: false, write: nil, reconnect: nil, close: nil, connect: nil) } - let(:delivery) { Rpush::Daemon::Apns::Delivery.new(app, connection, batch) } - - before do - allow(batch).to receive(:each_notification) do |&blk| - [notification1, notification2].each(&blk) - end - allow(Rpush).to receive_messages(logger: logger) - end - - it 'writes the binary batch' do - allow(notification1).to receive_messages(to_binary: 'binary1') - allow(notification2).to receive_messages(to_binary: 'binary2') - expect(connection).to receive(:write).with('binary1binary2') - delivery.perform - end - - it 'logs the notification deliveries' do - allow(notification1).to receive_messages(id: 666, device_token: 'abc123') - allow(notification2).to receive_messages(id: 42, device_token: 'abc456') - expect(logger).to receive(:info).with('[MyApp] 666 sent to abc123') - expect(logger).to receive(:info).with('[MyApp] 42 sent to abc456') - delivery.perform - end - - it 'marks all notifications as delivered' do - expect(delivery).to receive(:mark_batch_delivered) - delivery.perform - end - - it 'notifies the batch all notifications have been processed' do - expect(batch).to receive(:all_processed) - delivery.perform - end - - describe 'when an error is raised' do - it 'marks all notifications as failed' do - error = StandardError.new - allow(connection).to receive(:write).and_raise(error) - expect(delivery).to receive(:mark_batch_failed).with(error) - expect { delivery.perform }.to raise_error(error) - end - end - - # describe "when delivery fails" do - # before { connection.stub(select: true, read: [8, 4, 69].pack("ccN")) } - # - # it "marks the notification as failed" do - # delivery.should_receive(:mark_failed).with(4, "Unable to deliver notification 69, received error 4 (Missing payload)") - # perform - # end - # - # it "logs the delivery error" do - # # checking for the doublebed error doesn't work in jruby, but checking - # # for the exception by class does. - # - # # error = Rpush::DeliveryError.new(4, 12, "Missing payload") - # # Rpush::DeliveryError.stub(new: error) - # # expect { delivery.perform }.to raise_error(error) - # - # expect { delivery.perform }.to raise_error(Rpush::DeliveryError) - # end - # - # it "reads 6 bytes from the socket" do - # connection.should_receive(:read).with(6).and_return(nil) - # perform - # end - # - # it "does not attempt to read from the socket if the socket was not selected for reading after the timeout" do - # connection.stub(select: nil) - # connection.should_not_receive(:read) - # perform - # end - # - # it "reconnects the socket" do - # connection.should_receive(:reconnect) - # perform - # end - # - # it "logs that the connection is being reconnected" do - # Rpush.logger.should_receive(:error).with("[MyApp] Error received, reconnecting...") - # perform - # end - # - # context "when the APNs disconnects without returning an error" do - # before do - # connection.stub(read: nil) - # end - # - # it 'raises a DisconnectError error if the connection is closed without an error being returned' do - # expect { delivery.perform }.to raise_error(Rpush::DisconnectionError) - # end - # - # it 'marks the notification as failed' do - # delivery.should_receive(:mark_failed).with(nil, "The APNs disconnected without returning an error. This may indicate you are using an invalid certificate for the host.") - # perform - # end - # end - # end -end diff --git a/spec/unit/daemon/apns/feedback_receiver_spec.rb b/spec/unit/daemon/apns/feedback_receiver_spec.rb deleted file mode 100644 index eebbba40b..000000000 --- a/spec/unit/daemon/apns/feedback_receiver_spec.rb +++ /dev/null @@ -1,137 +0,0 @@ -require 'unit_spec_helper' -require 'rpush/daemon/store/active_record' - -describe Rpush::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do - let(:host) { 'feedback.push.apple.com' } - let(:port) { 2196 } - let(:frequency) { 60 } - let(:certificate) { double } - let(:password) { double } - let(:feedback_enabled) { true } - let(:app) do - double( - name: 'my_app', - password: password, - certificate: certificate, - feedback_enabled: feedback_enabled, - environment: 'production' - ) - end - let(:connection) { double(connect: nil, read: nil, close: nil) } - let(:logger) { double(error: nil, info: nil) } - let(:receiver) { Rpush::Daemon::Apns::FeedbackReceiver.new(app) } - let(:feedback) { double } - let(:sleeper) { double(Rpush::Daemon::InterruptibleSleep, sleep: nil, stop: nil) } - let(:store) { double(Rpush::Daemon::Store::ActiveRecord, create_apns_feedback: feedback, release_connection: nil) } - - before do - Rpush.config.apns.feedback_receiver.frequency = frequency - allow(Rpush::Daemon::InterruptibleSleep).to receive_messages(new: sleeper) - allow(Rpush).to receive_messages(logger: logger) - allow(Rpush::Daemon::TcpConnection).to receive_messages(new: connection) - receiver.instance_variable_set("@stop", false) - allow(Rpush::Daemon).to receive_messages(store: store) - end - - def double_connection_read_with_tuple - def connection.read(*) - unless @called - @called = true - "N\xE3\x84\r\x00 \x83OxfU\xEB\x9F\x84aJ\x05\xAD}\x00\xAF1\xE5\xCF\xE9:\xC3\xEA\a\x8F\x1D\xA4M*N\xB0\xCE\x17" - end - end - end - - it 'initializes the sleeper with the feedback polling frequency' do - expect(Rpush::Daemon::InterruptibleSleep).to receive_messages(new: sleeper) - Rpush::Daemon::Apns::FeedbackReceiver.new(app) - end - - it 'instantiates a new connection' do - expect(Rpush::Daemon::TcpConnection).to receive(:new).with(app, host, port) - receiver.check_for_feedback - end - - it 'connects to the feeback service' do - expect(connection).to receive(:connect) - receiver.check_for_feedback - end - - it 'closes the connection' do - expect(connection).to receive(:close) - receiver.check_for_feedback - end - - it 'reads from the connection' do - expect(connection).to receive(:read).with(38) - receiver.check_for_feedback - end - - it 'logs the feedback' do - double_connection_read_with_tuple - expect(Rpush.logger).to receive(:info).with("[my_app] [FeedbackReceiver] Delivery failed at 2011-12-10 16:08:45 UTC for 834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17.") - receiver.check_for_feedback - end - - it 'creates the feedback' do - expect(Rpush::Daemon.store).to receive(:create_apns_feedback).with(Time.at(1_323_533_325), '834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17', app) - double_connection_read_with_tuple - receiver.check_for_feedback - end - - it 'logs errors' do - error = StandardError.new('bork!') - allow(connection).to receive(:read).and_raise(error) - expect(Rpush.logger).to receive(:error).with(error) - receiver.check_for_feedback - end - - describe 'start' do - before do - allow(Thread).to receive(:new).and_yield - allow(receiver).to receive(:loop).and_yield - end - - it 'sleeps' do - allow(receiver).to receive(:check_for_feedback) - expect(sleeper).to receive(:sleep).at_least(:once) - receiver.start - end - - it 'checks for feedback when started' do - expect(receiver).to receive(:check_for_feedback).at_least(:once) - receiver.start - end - - context 'with feedback_enabled false' do - let(:feedback_enabled) { false } - - it 'does not check for feedback when started' do - expect(receiver).not_to receive(:check_for_feedback) - receiver.start - end - end - end - - describe 'stop' do - it 'interrupts sleep when stopped' do - allow(receiver).to receive(:check_for_feedback) - expect(sleeper).to receive(:stop) - receiver.stop - end - - it 'releases the store connection' do - allow(Thread).to receive(:new).and_yield - allow(receiver).to receive(:loop).and_yield - expect(Rpush::Daemon.store).to receive(:release_connection) - receiver.start - receiver.stop - end - end - - it 'reflects feedback was received' do - double_connection_read_with_tuple - expect(receiver).to receive(:reflect).with(:apns_feedback, feedback) - receiver.check_for_feedback - end -end diff --git a/spec/unit/daemon/dispatcher/tcp_spec.rb b/spec/unit/daemon/dispatcher/tcp_spec.rb deleted file mode 100644 index bbcac0952..000000000 --- a/spec/unit/daemon/dispatcher/tcp_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush::Daemon::Dispatcher::Tcp do - let(:app) { double } - let(:delivery) { double(perform: nil) } - let(:delivery_class) { double(new: delivery) } - let(:notification) { double } - let(:batch) { double } - let(:connection) { double(Rpush::Daemon::TcpConnection, connect: nil) } - let(:host) { 'localhost' } - let(:port) { 1234 } - let(:host_proc) { proc { [host, port] } } - let(:queue_payload) { Rpush::Daemon::QueuePayload.new(batch, notification) } - let(:dispatcher) { Rpush::Daemon::Dispatcher::Tcp.new(app, delivery_class, host: host_proc) } - - before { allow(Rpush::Daemon::TcpConnection).to receive_messages(new: connection) } - - describe 'dispatch' do - it 'delivers the notification' do - expect(delivery_class).to receive(:new).with(app, connection, notification, batch).and_return(delivery) - expect(delivery).to receive(:perform) - dispatcher.dispatch(queue_payload) - end - end - - describe 'cleanup' do - it 'closes the connection' do - expect(connection).to receive(:close) - dispatcher.cleanup - end - end -end diff --git a/spec/unit/daemon/gcm/delivery_spec.rb b/spec/unit/daemon/gcm/delivery_spec.rb deleted file mode 100644 index bcefa8999..000000000 --- a/spec/unit/daemon/gcm/delivery_spec.rb +++ /dev/null @@ -1,387 +0,0 @@ -require 'unit_spec_helper' - -describe Rpush::Daemon::Gcm::Delivery do - let(:app) { Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123') } - let(:notification) { Rpush::Gcm::Notification.create!(app: app, registration_ids: ['xyz'], deliver_after: Time.now) } - let(:logger) { double(error: nil, info: nil, warn: nil) } - let(:response) { double(code: 200, header: {}) } - let(:http) { double(shutdown: nil, request: response) } - let(:now) { Time.parse('2012-10-14 00:00:00') } - let(:batch) { double(mark_failed: nil, mark_delivered: nil, mark_retryable: nil, notification_processed: nil) } - let(:delivery) { Rpush::Daemon::Gcm::Delivery.new(app, http, notification, batch) } - let(:store) { double(create_gcm_notification: double(id: 2)) } - - def perform - delivery.perform - end - - def perform_with_rescue - expect { perform }.to raise_error(StandardError) - end - - before do - allow(delivery).to receive_messages(reflect: nil) - allow(Rpush::Daemon).to receive_messages(store: store) - allow(Time).to receive_messages(now: now) - allow(Rpush).to receive_messages(logger: logger) - end - - shared_examples_for 'a notification with some delivery failures' do - let(:new_notification) { Rpush::Gcm::Notification.where('id != ?', notification.id).first } - - before { allow(response).to receive_messages(body: JSON.dump(body)) } - - it 'marks the original notification as failed' do - # error = Rpush::DeliveryError.new(nil, notification.id, error_description) - expect(delivery).to receive(:mark_failed) do |error| - expect(error.to_s).to match(error_description) - end - perform_with_rescue - end - - it 'creates a new notification for the unavailable devices' do - notification.update(registration_ids: %w(id_0 id_1 id_2), data: { 'one' => 1 }, collapse_key: 'thing', delay_while_idle: true) - allow(response).to receive_messages(header: { 'retry-after' => 10 }) - attrs = { 'collapse_key' => 'thing', 'delay_while_idle' => true, 'app_id' => app.id } - expect(store).to receive(:create_gcm_notification).with(attrs, notification.data, - %w(id_0 id_2), now + 10.seconds, notification.app) - perform_with_rescue - end - - it 'raises a DeliveryError' do - expect { perform }.to raise_error(Rpush::DeliveryError) - end - end - - describe 'a 200 response' do - before do - allow(response).to receive_messages(code: 200) - end - - it 'reflects on any IDs which successfully received the notification' do - body = { - 'failure' => 1, - 'success' => 1, - 'results' => [ - { 'message_id' => '1:000' }, - { 'error' => 'Err' } - ] - } - - allow(response).to receive_messages(body: JSON.dump(body)) - allow(notification).to receive_messages(registration_ids: %w(1 2)) - expect(delivery).to receive(:reflect).with(:gcm_delivered_to_recipient, notification, '1') - expect(delivery).not_to receive(:reflect).with(:gcm_delivered_to_recipient, notification, '2') - perform_with_rescue - end - - it 'reflects on any IDs which failed to receive the notification' do - body = { - 'failure' => 1, - 'success' => 1, - 'results' => [ - { 'error' => 'Err' }, - { 'message_id' => '1:000' } - ] - } - - allow(response).to receive_messages(body: JSON.dump(body)) - allow(notification).to receive_messages(registration_ids: %w(1 2)) - expect(delivery).to receive(:reflect).with(:gcm_failed_to_recipient, notification, 'Err', '1') - expect(delivery).not_to receive(:reflect).with(:gcm_failed_to_recipient, notification, anything, '2') - perform_with_rescue - end - - it 'reflects on canonical IDs' do - body = { - 'failure' => 0, - 'success' => 3, - 'canonical_ids' => 1, - 'results' => [ - { 'message_id' => '1:000' }, - { 'message_id' => '1:000', 'registration_id' => 'canonical123' }, - { 'message_id' => '1:000' } - ] } - - allow(response).to receive_messages(body: JSON.dump(body)) - allow(notification).to receive_messages(registration_ids: %w(1 2 3)) - expect(delivery).to receive(:reflect).with(:gcm_canonical_id, '2', 'canonical123') - perform - end - - it 'reflects on invalid IDs' do - body = { - 'failure' => 1, - 'success' => 2, - 'canonical_ids' => 0, - 'results' => [ - { 'message_id' => '1:000' }, - { 'error' => 'NotRegistered' }, - { 'message_id' => '1:000' } - ] - } - - allow(response).to receive_messages(body: JSON.dump(body)) - allow(notification).to receive_messages(registration_ids: %w(1 2 3)) - expect(delivery).to receive(:reflect).with(:gcm_invalid_registration_id, app, 'NotRegistered', '2') - perform_with_rescue - end - - describe 'when delivered successfully to all devices' do - let(:body) do - { - 'failure' => 0, - 'success' => 1, - 'results' => [{ 'message_id' => '1:000' }] - } - end - - before { allow(response).to receive_messages(body: JSON.dump(body)) } - - it 'marks the notification as delivered' do - expect(delivery).to receive(:mark_delivered) - perform - end - - it 'logs that the notification was delivered' do - expect(logger).to receive(:info).with("[MyApp] #{notification.id} sent to xyz") - perform - end - end - - it 'marks a notification as failed if any ids are invalid' do - body = { - 'failure' => 1, - 'success' => 2, - 'canonical_ids' => 0, - 'results' => [ - { 'message_id' => '1:000' }, - { 'error' => 'NotRegistered' }, - { 'message_id' => '1:000' } - ] - } - - allow(response).to receive_messages(body: JSON.dump(body)) - expect(delivery).to receive(:mark_failed) - expect(delivery).not_to receive(:mark_retryable) - expect(store).not_to receive(:create_gcm_notification) - perform_with_rescue - end - - it 'marks a notification as failed if any deliveries failed that cannot be retried' do - body = { - 'failure' => 1, - 'success' => 1, - 'results' => [ - { 'message_id' => '1:000' }, - { 'error' => 'InvalidDataKey' } - ] } - allow(response).to receive_messages(body: JSON.dump(body)) - error = Rpush::DeliveryError.new(nil, notification.id, 'Failed to deliver to all recipients. Errors: InvalidDataKey.') - expect(delivery).to receive(:mark_failed).with(error) - perform_with_rescue - end - - describe 'all deliveries failed with Unavailable or InternalServerError' do - let(:body) do - { - 'failure' => 2, - 'success' => 0, - 'results' => [ - { 'error' => 'Unavailable' }, - { 'error' => 'Unavailable' } - ] - } - end - - before do - allow(response).to receive_messages(body: JSON.dump(body)) - allow(notification).to receive_messages(registration_ids: %w(1 2)) - end - - it 'retries the notification respecting the Retry-After header' do - allow(response).to receive_messages(header: { 'retry-after' => 10 }) - expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds) - perform - end - - it 'retries the notification using exponential back-off if the Retry-After header is not present' do - expect(delivery).to receive(:mark_retryable).with(notification, now + 2) - perform - end - - it 'does not mark the notification as failed' do - expect(delivery).not_to receive(:mark_failed) - perform - end - - it 'logs that the notification will be retried' do - notification.retries = 1 - notification.deliver_after = now + 2 - expect(Rpush.logger).to receive(:warn).with("[MyApp] All recipients unavailable. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).") - perform - end - end - - describe 'all deliveries failed with some as Unavailable or InternalServerError' do - let(:body) do - { 'failure' => 3, - 'success' => 0, - 'results' => [ - { 'error' => 'Unavailable' }, - { 'error' => 'InvalidDataKey' }, - { 'error' => 'Unavailable' } - ] - } - end - let(:error_description) { /#{Regexp.escape("Failed to deliver to recipients 0, 1, 2. Errors: Unavailable, InvalidDataKey, Unavailable. 0, 2 will be retried as notification")} [\d]+\./ } - it_should_behave_like 'a notification with some delivery failures' - end - - describe 'some deliveries failed with Unavailable or InternalServerError' do - let(:body) do - { 'failure' => 2, - 'success' => 1, - 'results' => [ - { 'error' => 'Unavailable' }, - { 'message_id' => '1:000' }, - { 'error' => 'InternalServerError' } - ] - } - end - let(:error_description) { /#{Regexp.escape("Failed to deliver to recipients 0, 2. Errors: Unavailable, InternalServerError. 0, 2 will be retried as notification")} [\d]+\./ } - it_should_behave_like 'a notification with some delivery failures' - end - end - - describe 'a 503 response' do - before { allow(response).to receive_messages(code: 503) } - - it 'logs a warning that the notification will be retried.' do - notification.retries = 1 - notification.deliver_after = now + 2 - expect(logger).to receive(:warn).with("[MyApp] GCM responded with an Service Unavailable Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).") - perform - end - - it 'respects an integer Retry-After header' do - allow(response).to receive_messages(header: { 'retry-after' => 10 }) - expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds) - perform - end - - it 'respects a HTTP-date Retry-After header' do - allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' }) - expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT')) - perform - end - - it 'defaults to exponential back-off if the Retry-After header is not present' do - expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1) - perform - end - end - - describe 'a 502 response' do - before { allow(response).to receive_messages(code: 502) } - - it 'logs a warning that the notification will be retried.' do - notification.retries = 1 - notification.deliver_after = now + 2 - expect(logger).to receive(:warn).with("[MyApp] GCM responded with a Bad Gateway Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).") - perform - end - - it 'respects an integer Retry-After header' do - allow(response).to receive_messages(header: { 'retry-after' => 10 }) - expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds) - perform - end - - it 'respects a HTTP-date Retry-After header' do - allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' }) - expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT')) - perform - end - - it 'defaults to exponential back-off if the Retry-After header is not present' do - expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1) - perform - end - end - - describe 'a 500 response' do - before do - notification.update_attribute(:retries, 2) - allow(response).to receive_messages(code: 500) - end - - it 'logs a warning that the notification has been re-queued.' do - notification.retries = 3 - notification.deliver_after = now + 2**3 - expect(Rpush.logger).to receive(:warn).with("[MyApp] GCM responded with an Internal Error. Notification #{notification.id} will be retried after #{(now + 2**3).strftime('%Y-%m-%d %H:%M:%S')} (retry 3).") - perform - end - - it 'retries the notification in accordance with the exponential back-off strategy.' do - expect(delivery).to receive(:mark_retryable).with(notification, now + 2**3) - perform - end - end - - describe 'a 5xx response' do - before { allow(response).to receive_messages(code: 555) } - - it 'logs a warning that the notification will be retried.' do - notification.retries = 1 - notification.deliver_after = now + 2 - expect(logger).to receive(:warn).with("[MyApp] GCM responded with a 5xx Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).") - perform - end - - it 'respects an integer Retry-After header' do - allow(response).to receive_messages(header: { 'retry-after' => 10 }) - expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds) - perform - end - - it 'respects a HTTP-date Retry-After header' do - allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' }) - expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT')) - perform - end - - it 'defaults to exponential back-off if the Retry-After header is not present' do - expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1) - perform - end - end - - describe 'a 401 response' do - before { allow(response).to receive_messages(code: 401) } - - it 'raises an error' do - expect { perform }.to raise_error(Rpush::DeliveryError) - end - end - - describe 'a 400 response' do - before { allow(response).to receive_messages(code: 400) } - - it 'marks the notification as failed' do - error = Rpush::DeliveryError.new(400, notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.') - expect(delivery).to receive(:mark_failed).with(error) - perform_with_rescue - end - end - - describe 'an un-handled response' do - before { allow(response).to receive_messages(code: 418) } - - it 'marks the notification as failed' do - error = Rpush::DeliveryError.new(418, notification.id, "I'm a Teapot") - expect(delivery).to receive(:mark_failed).with(error) - perform_with_rescue - end - end -end diff --git a/spec/unit/daemon/shared/store.rb b/spec/unit/daemon/shared/store.rb index c3d67f97d..7a97e6eee 100644 --- a/spec/unit/daemon/shared/store.rb +++ b/spec/unit/daemon/shared/store.rb @@ -233,48 +233,6 @@ end end - describe 'create_apns_feedback' do - it 'creates the Feedback record' do - expect(Rpush::Apns::Feedback).to receive(:create!).with( - failed_at: time, device_token: 'ab' * 32, app_id: app.id - ) - store.create_apns_feedback(time, 'ab' * 32, app) - end - end - - describe 'create_gcm_notification' do - let(:data) { { 'data' => true } } - let(:attributes) { { device_token: 'ab' * 32 } } - let(:registration_ids) { %w[123 456] } - let(:deliver_after) { time + 10.seconds } - let(:args) { [attributes, data, registration_ids, deliver_after, app] } - - it 'sets the given attributes' do - new_notification = store.create_gcm_notification(*args) - expect(new_notification.device_token).to eq 'ab' * 32 - end - - it 'sets the given data' do - new_notification = store.create_gcm_notification(*args) - expect(new_notification.data['data']).to be_truthy - end - - it 'sets the given registration IDs' do - new_notification = store.create_gcm_notification(*args) - expect(new_notification.registration_ids).to eq registration_ids - end - - it 'sets the deliver_after timestamp' do - new_notification = store.create_gcm_notification(*args) - expect(new_notification.deliver_after).to eq deliver_after - end - - it 'saves the new notification' do - new_notification = store.create_gcm_notification(*args) - expect(new_notification.new_record?).to be_falsey - end - end - describe 'create_adm_notification' do let(:data) { { 'data' => true } } let(:attributes) { { app_id: app.id, collapse_key: 'ckey', delay_while_idle: true } } diff --git a/spec/unit/daemon/tcp_connection_spec.rb b/spec/unit/daemon/tcp_connection_spec.rb deleted file mode 100644 index f83c26cfb..000000000 --- a/spec/unit/daemon/tcp_connection_spec.rb +++ /dev/null @@ -1,293 +0,0 @@ -require "unit_spec_helper" - -describe Rpush::Daemon::TcpConnection do - let(:rsa_key) { double } - let(:certificate) { double } - let(:password) { double } - let(:x509_certificate) { OpenSSL::X509::Certificate.new(TEST_CERT) } - let(:ssl_context) { double(:key= => nil, :cert= => nil, cert: x509_certificate) } - let(:host) { 'gateway.push.apple.com' } - let(:port) { '2195' } - let(:tcp_socket) { double(setsockopt: nil, close: nil) } - let(:ssl_socket) { double(:sync= => nil, connect: nil, close: nil, write: nil, flush: nil) } - let(:logger) { double(info: nil, error: nil, warn: nil) } - let(:app) { double(name: 'Connection 0', certificate: certificate, password: password) } - let(:connection) { Rpush::Daemon::TcpConnection.new(app, host, port) } - - before do - allow(x509_certificate).to receive(:not_after).and_return(Time.now + 1.year) - allow(OpenSSL::SSL::SSLContext).to receive_messages(new: ssl_context) - allow(OpenSSL::PKey::RSA).to receive_messages(new: rsa_key) - allow(OpenSSL::X509::Certificate).to receive_messages(new: x509_certificate) - allow(TCPSocket).to receive_messages(new: tcp_socket) - allow(OpenSSL::SSL::SSLSocket).to receive_messages(new: ssl_socket) - allow(Rpush).to receive_messages(logger: logger) - allow(connection).to receive(:reflect) - end - - it "reads the number of bytes from the SSL socket" do - expect(ssl_socket).to receive(:read).with(123) - connection.connect - connection.read(123) - end - - it "selects on the SSL socket until the given timeout" do - expect(IO).to receive(:select).with([ssl_socket], nil, nil, 10) - connection.connect - connection.select(10) - end - - describe "when setting up the SSL context" do - it "sets the key on the context" do - expect(OpenSSL::PKey::RSA).to receive(:new).with(certificate, password).and_return(rsa_key) - expect(ssl_context).to receive(:key=).with(rsa_key) - connection.connect - end - - it "sets the cert on the context" do - expect(OpenSSL::X509::Certificate).to receive(:new).with(certificate).and_return(x509_certificate) - expect(ssl_context).to receive(:cert=).with(x509_certificate) - connection.connect - end - end - - describe "when connecting the socket" do - it "creates a TCP socket using the configured host and port" do - expect(TCPSocket).to receive(:new).with(host, port).and_return(tcp_socket) - connection.connect - end - - it "creates a new SSL socket using the TCP socket and SSL context" do - expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_context).and_return(ssl_socket) - connection.connect - end - - it "sets the sync option on the SSL socket" do - expect(ssl_socket).to receive(:sync=).with(true) - connection.connect - end - - it "connects the SSL socket" do - expect(ssl_socket).to receive(:connect) - connection.connect - end - - it "sets the socket option TCP_NODELAY" do - expect(tcp_socket).to receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true) - connection.connect - end - - it "sets the socket option SO_KEEPALIVE" do - expect(tcp_socket).to receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) - connection.connect - end - - describe 'certificate expiry' do - it 'reflects if the certificate will expire soon' do - cert = x509_certificate - expect(connection).to receive(:reflect).with(:ssl_certificate_will_expire, app, cert.not_after) - Timecop.freeze(cert.not_after - 3.days) { connection.connect } - end - - it 'logs that the certificate will expire soon' do - cert = x509_certificate - expect(logger).to receive(:warn).with("[#{app.name}] Certificate will expire at #{cert.not_after.utc}.") - Timecop.freeze(cert.not_after - 3.days) { connection.connect } - end - - it 'does not reflect if the certificate will not expire soon' do - cert = x509_certificate - expect(connection).not_to receive(:reflect).with(:ssl_certificate_will_expire, app, kind_of(Time)) - Timecop.freeze(cert.not_after - 2.months) { connection.connect } - end - - it 'logs that the certificate has expired' do - cert = x509_certificate - expect(logger).to receive(:error).with("[#{app.name}] Certificate expired at #{cert.not_after.utc}.") - Timecop.freeze(cert.not_after + 1.day) { connection.connect rescue Rpush::CertificateExpiredError } - end - - it 'raises an error if the certificate has expired' do - cert = x509_certificate - Timecop.freeze(cert.not_after + 1.day) do - expect { connection.connect }.to raise_error(Rpush::CertificateExpiredError) - end - end - end - - describe 'certificate revocation' do - let(:cert_revoked_error) { OpenSSL::SSL::SSLError.new('certificate revoked') } - before do - allow(ssl_socket).to receive(:connect).and_raise(cert_revoked_error) - end - - it 'reflects that the certificate has been revoked' do - expect(connection).to receive(:reflect).with(:ssl_certificate_revoked, app, cert_revoked_error) - expect { connection.connect }.to raise_error(Rpush::Daemon::TcpConnectionError, 'OpenSSL::SSL::SSLError, certificate revoked') - end - - it 'logs that the certificate has been revoked' do - expect(logger).to receive(:error).with('[Connection 0] Certificate has been revoked.') - expect { connection.connect }.to raise_error(Rpush::Daemon::TcpConnectionError, 'OpenSSL::SSL::SSLError, certificate revoked') - end - end - end - - describe "when shuting down the connection" do - it "closes the TCP socket" do - connection.connect - expect(tcp_socket).to receive(:close) - connection.close - end - - it "does not attempt to close the TCP socket if it is not connected" do - connection.connect - expect(tcp_socket).not_to receive(:close) - connection.instance_variable_set("@tcp_socket", nil) - connection.close - end - - it "closes the SSL socket" do - connection.connect - expect(ssl_socket).to receive(:close) - connection.close - end - - it "does not attempt to close the SSL socket if it is not connected" do - connection.connect - expect(ssl_socket).not_to receive(:close) - connection.instance_variable_set("@ssl_socket", nil) - connection.close - end - - it "ignores IOError when the socket is already closed" do - allow(tcp_socket).to receive(:close).and_raise(IOError) - connection.connect - connection.close - end - end - - shared_examples_for "when the write fails" do - before do - allow(connection).to receive(:sleep) - connection.connect - allow(ssl_socket).to receive(:write).and_raise(error) - end - - it 'reflects the connection has been lost' do - expect(connection).to receive(:reflect).with(:tcp_connection_lost, app, kind_of(error.class)) - expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError) - end - - it "logs that the connection has been lost once only" do - expect(logger).to receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error.class.name}, #{error.message}), reconnecting...").once - expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError) - end - - it "retries to make a connection 3 times" do - expect(connection).to receive(:reconnect).exactly(3).times - expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError) - end - - it "raises a TcpConnectionError after 3 attempts at reconnecting" do - expect do - connection.write(nil) - end.to raise_error(Rpush::Daemon::TcpConnectionError, "Connection 0 tried 3 times to reconnect but failed (#{error.class.name}, #{error.message}).") - end - - it "sleeps 1 second before retrying the connection" do - expect(connection).to receive(:sleep).with(1) - expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError) - end - end - - describe "when write raises an Errno::EPIPE" do - it_should_behave_like "when the write fails" - - def error - Errno::EPIPE.new('an message') - end - end - - describe "when write raises an Errno::ETIMEDOUT" do - it_should_behave_like "when the write fails" - - def error - Errno::ETIMEDOUT.new('an message') - end - end - - describe "when write raises an OpenSSL::SSL::SSLError" do - it_should_behave_like "when the write fails" - - def error - OpenSSL::SSL::SSLError.new('an message') - end - end - - describe "when write raises an IOError" do - it_should_behave_like "when the write fails" - - def error - IOError.new('an message') - end - end - - describe "when reconnecting" do - before { connection.connect } - - it 'closes the socket' do - expect(connection).to receive(:close) - connection.send(:reconnect) - end - - it 'connects the socket' do - expect(connection).to receive(:connect_socket) - connection.send(:reconnect) - end - end - - describe "when sending a notification" do - before { connection.connect } - - it "writes the data to the SSL socket" do - expect(ssl_socket).to receive(:write).with("blah") - connection.write("blah") - end - - it "flushes the SSL socket" do - expect(ssl_socket).to receive(:flush) - connection.write("blah") - end - end - - describe 'idle period' do - before { connection.connect } - - it 'reconnects if the connection has been idle for more than the defined period' do - allow(Rpush::Daemon::TcpConnection).to receive_messages(idle_period: 60) - allow(Time).to receive_messages(now: Time.now + 61) - expect(connection).to receive(:reconnect) - connection.write('blah') - end - - it 'resets the last touch time' do - now = Time.now - allow(Time).to receive_messages(now: now) - connection.write('blah') - expect(connection.last_touch).to eq now - end - - it 'does not reconnect if the connection has not been idle for more than the defined period' do - expect(connection).not_to receive(:reconnect) - connection.write('blah') - end - - it 'logs the the connection is idle' do - allow(Rpush::Daemon::TcpConnection).to receive_messages(idle_period: 60) - allow(Time).to receive_messages(now: Time.now + 61) - expect(Rpush.logger).to receive(:info).with('[Connection 0] Idle period exceeded, reconnecting...') - connection.write('blah') - end - end -end