diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c67b2c5..105e762 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,7 @@ jobs: - "2.7" - "3.0" - "3.1" + - "3.2" - "jruby-9.3.10.0" - "jruby-9.4.1.0" steps: diff --git a/.rubocop.yml b/.rubocop.yml index b25d64d..cee8e01 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,5 @@ AllCops: + TargetRubyVersion: 2.6 Exclude: - 'spec/support/*' - 'bin/**/*' @@ -7,6 +8,8 @@ Style/Documentation: Enabled: false Style/FrozenStringLiteralComment: Enabled: false +Style/StringLiterals: + EnforcedStyle: double_quotes Style/NumericPredicate: Enabled: false Lint/UnifiedInteger: @@ -19,6 +22,7 @@ Style/PercentLiteralDelimiters: Metrics/BlockLength: Exclude: - 'spec/**/*' + - '*.gemspec' Security/MarshalLoad: Exclude: - 'lib/candy_check/play_store/discovery_repository.rb' @@ -28,3 +32,9 @@ Style/Encoding: Enabled: false Style/ExpandPathArguments: Enabled: false +Style/TrailingCommaInArrayLiteral: + EnforcedStyleForMultiline: comma +Style/TrailingCommaInArguments: + EnforcedStyleForMultiline: comma +Style/TrailingCommaInHashLiteral: + EnforcedStyleForMultiline: comma diff --git a/.ruby-version b/.ruby-version index ff365e0..e4604e3 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.1.3 +3.2.1 diff --git a/Gemfile b/Gemfile index f1d387a..86fffea 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source 'https://rubygems.org' +source "https://rubygems.org" # Specify your gem's dependencies in candy_check.gemspec gemspec diff --git a/Guardfile b/Guardfile index 65daff3..2037978 100644 --- a/Guardfile +++ b/Guardfile @@ -2,8 +2,10 @@ # More info at https://github.com/guard/guard#readme ## Uncomment and set this to only include directories you want to watch -directories %w(lib spec) \ - .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")} +directories( + %w(lib spec) \ + .select { |d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist") }, +) ## Note: if you are using the `directories` clause above and you are not ## watching the project directory ('.'), then you will want to move @@ -24,7 +26,7 @@ guard :minitest do # with Minitest::Spec watch(%r{^spec/(.*)_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } - watch(%r{^spec/spec_helper\.rb$}) { 'spec' } + watch(%r{^spec/spec_helper\.rb$}) { "spec" } # Rails 4 # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" } diff --git a/Rakefile b/Rakefile index eb37202..4abff26 100755 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,12 @@ #!/usr/bin/env rake -require 'bundler/gem_tasks' -require 'rake/testtask' -require 'rubocop/rake_task' +require "bundler/gem_tasks" +require "rake/testtask" +require "rubocop/rake_task" Rake::TestTask.new(:spec) do |test| - test.test_files = FileList['spec/**/*_spec.rb'] - test.libs << 'spec' + test.test_files = FileList["spec/**/*_spec.rb"] + test.libs << "spec" test.verbose = true end diff --git a/candy_check.gemspec b/candy_check.gemspec index c866a47..999f26b 100644 --- a/candy_check.gemspec +++ b/candy_check.gemspec @@ -25,20 +25,20 @@ Gem::Specification.new do |spec| spec.add_dependency "multi_json", "~> 1.15" spec.add_dependency "thor", "< 2.0" + spec.add_development_dependency "awesome_print" spec.add_development_dependency "bundler", "~> 2.0" + spec.add_development_dependency "guard" + spec.add_development_dependency "guard-minitest" spec.add_development_dependency "inch", "~> 0.7" spec.add_development_dependency "minitest", "~> 5.10" spec.add_development_dependency "minitest-around", "~> 0.4" spec.add_development_dependency "minitest-focus" - spec.add_development_dependency "rake", "~> 12.0" - spec.add_development_dependency "rubocop", "~> 0.48" - spec.add_development_dependency "timecop", "~> 0.8" - spec.add_development_dependency "webmock", "~> 3.0" - spec.add_development_dependency "vcr" spec.add_development_dependency "pry" - spec.add_development_dependency "awesome_print" - spec.add_development_dependency "guard" - spec.add_development_dependency "guard-minitest" + spec.add_development_dependency "rake", "~> 13.0" + spec.add_development_dependency "rubocop", "~> 1.46" spec.add_development_dependency "simplecov", "~> 0.18.0" spec.add_development_dependency "simplecov-lcov", "~> 0.8.0" + spec.add_development_dependency "timecop", "~> 0.9" + spec.add_development_dependency "vcr", "~> 6.1" + spec.add_development_dependency "webmock", "~> 3.0" end diff --git a/lib/candy_check.rb b/lib/candy_check.rb index ac8ff9c..075f359 100644 --- a/lib/candy_check.rb +++ b/lib/candy_check.rb @@ -1,7 +1,7 @@ -require 'candy_check/version' -require 'candy_check/utils' -require 'candy_check/app_store' -require 'candy_check/play_store' +require "candy_check/version" +require "candy_check/utils" +require "candy_check/app_store" +require "candy_check/play_store" # Module to check and verify in-app receipts module CandyCheck diff --git a/lib/candy_check/app_store.rb b/lib/candy_check/app_store.rb index e4f1586..c6d56b4 100644 --- a/lib/candy_check/app_store.rb +++ b/lib/candy_check/app_store.rb @@ -1,11 +1,11 @@ -require 'candy_check/app_store/client' -require 'candy_check/app_store/config' -require 'candy_check/app_store/receipt' -require 'candy_check/app_store/receipt_collection' -require 'candy_check/app_store/verification' -require 'candy_check/app_store/subscription_verification' -require 'candy_check/app_store/verification_failure' -require 'candy_check/app_store/verifier' +require "candy_check/app_store/client" +require "candy_check/app_store/config" +require "candy_check/app_store/receipt" +require "candy_check/app_store/receipt_collection" +require "candy_check/app_store/verification" +require "candy_check/app_store/subscription_verification" +require "candy_check/app_store/verification_failure" +require "candy_check/app_store/verifier" module CandyCheck # Module to request and verify a AppStore receipt diff --git a/lib/candy_check/app_store/client.rb b/lib/candy_check/app_store/client.rb index a1b0bd0..25c3336 100644 --- a/lib/candy_check/app_store/client.rb +++ b/lib/candy_check/app_store/client.rb @@ -1,5 +1,5 @@ -require 'multi_json' -require 'net/http' +require "multi_json" +require "net/http" module CandyCheck module AppStore @@ -7,7 +7,7 @@ module AppStore # servers (either sandbox or production). class Client # Mimetype for JSON objects - JSON_MIME_TYPE = 'application/json'.freeze + JSON_MIME_TYPE = "application/json".freeze # Initialize a new client bound to an endpoint # @param endpoint_url [String] @@ -40,17 +40,17 @@ def build_http_connector def build_request(parameters) Net::HTTP::Post.new(@uri.request_uri).tap do |post| - post['Accept'] = JSON_MIME_TYPE - post['Content-Type'] = JSON_MIME_TYPE + post["Accept"] = JSON_MIME_TYPE + post["Content-Type"] = JSON_MIME_TYPE post.body = MultiJson.dump(parameters) end end def build_request_parameters(receipt_data, secret) { - 'receipt-data' => receipt_data + "receipt-data" => receipt_data, }.tap do |h| - h['password'] = secret if secret + h["password"] = secret if secret end end end diff --git a/lib/candy_check/app_store/config.rb b/lib/candy_check/app_store/config.rb index 2f70c08..f2e5d24 100644 --- a/lib/candy_check/app_store/config.rb +++ b/lib/candy_check/app_store/config.rb @@ -5,15 +5,13 @@ class Config < Utils::Config # @return [Symbol] the used environment attr_reader :environment - # Initializes a new configuration from a hash - # @param attributes [Hash] - # @example - # Config.new( - # environment: :production # or :sandbox - # ) - def initialize(attributes) - super - end + # @!method initialize(attributes) + # Initializes a new configuration from a hash + # @param attributes [Hash] + # @example + # Config.new( + # environment: :production # or :sandbox + # ) # @return [Boolean] if it is production environment def production? diff --git a/lib/candy_check/app_store/receipt.rb b/lib/candy_check/app_store/receipt.rb index 375843b..b071d9b 100644 --- a/lib/candy_check/app_store/receipt.rb +++ b/lib/candy_check/app_store/receipt.rb @@ -18,63 +18,63 @@ def initialize(attributes) # transaction was canceled. # @return [Boolean] def valid? - !has?('cancellation_date') + !has?("cancellation_date") end # The receipt's transaction id # @return [String] def transaction_id - read('transaction_id') + read("transaction_id") end # The receipt's original transaction id which might differ from # the transaction id for restored products # @return [String] def original_transaction_id - read('original_transaction_id') + read("original_transaction_id") end # The version number for the app # @return [String] def app_version - read('bvrs') + read("bvrs") end # The app's bundle identifier # @return [String] def bundle_identifier - read('bid') + read("bid") end # The app's identifier of the product (SKU) # @return [String] def product_id - read('product_id') + read("product_id") end # The app's item id of the product # @return [String] def item_id - read('item_id') + read("item_id") end # The quantity of the product # @return [Integer] def quantity - read_integer('quantity') + read_integer("quantity") end # The purchase date # @return [DateTime] def purchase_date - read_datetime_from_string('purchase_date') + read_datetime_from_string("purchase_date") end # The original purchase date which might differ from the # actual purchase date for restored products # @return [DateTime] def original_purchase_date - read_datetime_from_string('original_purchase_date') + read_datetime_from_string("original_purchase_date") end # The date of when Apple has canceled this transaction. @@ -82,19 +82,19 @@ def original_purchase_date # the same as if no purchase had ever been made." # @return [DateTime] def cancellation_date - read_datetime_from_string('cancellation_date') + read_datetime_from_string("cancellation_date") end # The date of a subscription's expiration # @return [DateTime] def expires_date - read_datetime_from_string('expires_date') + read_datetime_from_string("expires_date") end - # rubocop:disable PredicateName + # rubocop:disable Naming/PredicateName def is_trial_period - # rubocop:enable PredicateName - read_bool('is_trial_period') + # rubocop:enable Naming/PredicateName + read_bool("is_trial_period") end end end diff --git a/lib/candy_check/app_store/receipt_collection.rb b/lib/candy_check/app_store/receipt_collection.rb index 2cd21df..fe7741a 100644 --- a/lib/candy_check/app_store/receipt_collection.rb +++ b/lib/candy_check/app_store/receipt_collection.rb @@ -10,9 +10,9 @@ class ReceiptCollection # from Apple's verification server # @param attributes [Array] raw data from Apple's server def initialize(attributes) - @receipts = attributes.map {|r| Receipt.new(r) }.sort{ |a, b| + @receipts = attributes.map { |r| Receipt.new(r) }.sort do |a, b| a.purchase_date - b.purchase_date - } + end end # Check if the latest expiration date is passed diff --git a/lib/candy_check/app_store/subscription_verification.rb b/lib/candy_check/app_store/subscription_verification.rb index 2e9a615..72bdc44 100644 --- a/lib/candy_check/app_store/subscription_verification.rb +++ b/lib/candy_check/app_store/subscription_verification.rb @@ -24,9 +24,9 @@ def initialize( def call! verify! if valid? - build_collection(@response['latest_receipt_info']) + build_collection(@response["latest_receipt_info"]) else - VerificationFailure.fetch(@response['status']) + VerificationFailure.fetch(@response["status"]) end end @@ -35,15 +35,15 @@ def call! def build_collection(latest_receipt_info) unless @product_ids.nil? latest_receipt_info = latest_receipt_info.select do |info| - @product_ids.include?(info['product_id']) + @product_ids.include?(info["product_id"]) end end ReceiptCollection.new(latest_receipt_info) end def valid? - status_is_ok = @response['status'] == STATUS_OK - @response && status_is_ok && @response['latest_receipt_info'] + status_is_ok = @response["status"] == STATUS_OK + @response && status_is_ok && @response["latest_receipt_info"] end end end diff --git a/lib/candy_check/app_store/verification.rb b/lib/candy_check/app_store/verification.rb index b7536d0..90e2f4d 100644 --- a/lib/candy_check/app_store/verification.rb +++ b/lib/candy_check/app_store/verification.rb @@ -29,16 +29,16 @@ def initialize(endpoint_url, receipt_data, secret = nil) def call! verify! if valid? - Receipt.new(@response['receipt']) + Receipt.new(@response["receipt"]) else - VerificationFailure.fetch(@response['status']) + VerificationFailure.fetch(@response["status"]) end end private def valid? - @response && @response['status'] == STATUS_OK && @response['receipt'] + @response && @response["status"] == STATUS_OK && @response["receipt"] end def verify! diff --git a/lib/candy_check/app_store/verification_failure.rb b/lib/candy_check/app_store/verification_failure.rb index cb11bf0..3a1fde4 100644 --- a/lib/candy_check/app_store/verification_failure.rb +++ b/lib/candy_check/app_store/verification_failure.rb @@ -31,7 +31,7 @@ def fetch(code) private def fallback(code) - new(code || -1, 'Unknown error') + new(code || -1, "Unknown error") end def known @@ -47,27 +47,27 @@ def freeze! end end - add 21_000, 'The request to the App Store was not made using' \ - ' the HTTP POST request method.' - add 21_001, 'This status code is no longer sent by the App Store.' - add 21_002, 'The data in the receipt-data property was malformed' \ - ' or the service experienced a temporary issue. Try again.' - add 21_003, 'The receipt could not be authenticated.' - add 21_004, 'The shared secret you provided does not match the shared' \ - ' secret on file for your account.' - add 21_005, 'The receipt server was temporarily unable to provide' \ - ' the receipt. Try again.' - add 21_006, 'This receipt is valid but the subscription has expired.' \ - ' When this status code is returned to your server, the' \ - ' receipt data is also decoded and returned as part of' \ - ' the response. Only returned for iOS 6-style transaction' \ - ' receipts for auto-renewable subscriptions.' - add 21_007, 'This receipt is from the test environment, but it was' \ - ' sent to the production environment for verification.' - add 21_008, 'This receipt is from the production environment, but it' \ - ' was sent to the test environment for verification.' - add 21_009, 'Internal data access error. Try again later.' - add 21_010, 'The user account cannot be found or has been deleted.' + add 21_000, "The request to the App Store was not made using" \ + " the HTTP POST request method." + add 21_001, "This status code is no longer sent by the App Store." + add 21_002, "The data in the receipt-data property was malformed" \ + " or the service experienced a temporary issue. Try again." + add 21_003, "The receipt could not be authenticated." + add 21_004, "The shared secret you provided does not match the shared" \ + " secret on file for your account." + add 21_005, "The receipt server was temporarily unable to provide" \ + " the receipt. Try again." + add 21_006, "This receipt is valid but the subscription has expired." \ + " When this status code is returned to your server, the" \ + " receipt data is also decoded and returned as part of" \ + " the response. Only returned for iOS 6-style transaction" \ + " receipts for auto-renewable subscriptions." + add 21_007, "This receipt is from the test environment, but it was" \ + " sent to the production environment for verification." + add 21_008, "This receipt is from the production environment, but it" \ + " was sent to the test environment for verification." + add 21_009, "Internal data access error. Try again later." + add 21_010, "The user account cannot be found or has been deleted." freeze! end end diff --git a/lib/candy_check/app_store/verifier.rb b/lib/candy_check/app_store/verifier.rb index 283117b..596fc8c 100644 --- a/lib/candy_check/app_store/verifier.rb +++ b/lib/candy_check/app_store/verifier.rb @@ -4,9 +4,9 @@ module AppStore # The call return either an {Receipt} or a {VerificationFailure} class Verifier # HTTPS endpoint for production receipts - PRODUCTION_ENDPOINT = 'https://buy.itunes.apple.com/verifyReceipt'.freeze + PRODUCTION_ENDPOINT = "https://buy.itunes.apple.com/verifyReceipt".freeze # HTTPS endpoint for sandbox receipts - SANDBOX_ENDPOINT = 'https://sandbox.itunes.apple.com/verifyReceipt'.freeze + SANDBOX_ENDPOINT = "https://sandbox.itunes.apple.com/verifyReceipt".freeze # Status code from production endpoint when receiving a sandbox # receipt which occurs during the app's review process REDIRECT_TO_SANDBOX_CODE = 21_007 @@ -49,9 +49,8 @@ def verify_subscription(receipt_data, secret = nil, product_ids = nil) def fetch_receipt_information(verifier_class, args) default_endpoint, opposite_endpoint = endpoints result = call_for(verifier_class, args.dup.unshift(default_endpoint)) - if should_retry?(result) - return call_for(verifier_class, args.dup.unshift(opposite_endpoint)) - end + return call_for(verifier_class, args.dup.unshift(opposite_endpoint)) if should_retry?(result) + result end diff --git a/lib/candy_check/cli.rb b/lib/candy_check/cli.rb index 1e44346..d50f3d3 100644 --- a/lib/candy_check/cli.rb +++ b/lib/candy_check/cli.rb @@ -1,6 +1,6 @@ -require 'candy_check/cli/app' -require 'candy_check/cli/commands' -require 'candy_check/cli/out' +require "candy_check/cli/app" +require "candy_check/cli/commands" +require "candy_check/cli/out" module CandyCheck # Namespace holding the implementation of the CLI to diff --git a/lib/candy_check/cli/app.rb b/lib/candy_check/cli/app.rb index 6a19f94..866fbc2 100644 --- a/lib/candy_check/cli/app.rb +++ b/lib/candy_check/cli/app.rb @@ -35,7 +35,7 @@ def play_store(package, product_id, token) Commands::PlayStore.run(package, product_id, token, **options) end - desc "version", 'Print the gem\'s version' + desc "version", "Print the gem's version" def version Commands::Version.run diff --git a/lib/candy_check/cli/commands.rb b/lib/candy_check/cli/commands.rb index 39907bc..ed01f6b 100644 --- a/lib/candy_check/cli/commands.rb +++ b/lib/candy_check/cli/commands.rb @@ -1,7 +1,7 @@ -require 'candy_check/cli/commands/base' -require 'candy_check/cli/commands/app_store' -require 'candy_check/cli/commands/play_store' -require 'candy_check/cli/commands/version' +require "candy_check/cli/commands/base" +require "candy_check/cli/commands/app_store" +require "candy_check/cli/commands/play_store" +require "candy_check/cli/commands/version" module CandyCheck module CLI diff --git a/lib/candy_check/cli/commands/app_store.rb b/lib/candy_check/cli/commands/app_store.rb index 12d43a4..fd34e64 100644 --- a/lib/candy_check/cli/commands/app_store.rb +++ b/lib/candy_check/cli/commands/app_store.rb @@ -25,7 +25,7 @@ def run def config CandyCheck::AppStore::Config.new( - environment: options[:environment].to_sym + environment: options[:environment].to_sym, ) end end diff --git a/lib/candy_check/cli/out.rb b/lib/candy_check/cli/out.rb index 24aeb38..c8c5fc0 100644 --- a/lib/candy_check/cli/out.rb +++ b/lib/candy_check/cli/out.rb @@ -1,5 +1,3 @@ -require 'pp' - module CandyCheck module CLI # A wrapper to output text information to any kind of buffer @@ -18,7 +16,7 @@ def initialize(out = $stdout) # Prints to +out+ # @param text [String] - def print(text = '') + def print(text = "") out.puts text end diff --git a/lib/candy_check/play_store/product_acknowledgements/acknowledgement.rb b/lib/candy_check/play_store/product_acknowledgements/acknowledgement.rb index 99733ed..a4e2b06 100644 --- a/lib/candy_check/play_store/product_acknowledgements/acknowledgement.rb +++ b/lib/candy_check/play_store/product_acknowledgements/acknowledgement.rb @@ -26,7 +26,8 @@ def call! acknowledge! CandyCheck::PlayStore::ProductAcknowledgements::Response.new( - result: @response[:result], error_data: @response[:error_data]) + result: @response[:result], error_data: @response[:error_data], + ) end private diff --git a/lib/candy_check/play_store/product_purchases/product_verification.rb b/lib/candy_check/play_store/product_purchases/product_verification.rb index 727f509..657c454 100644 --- a/lib/candy_check/play_store/product_purchases/product_verification.rb +++ b/lib/candy_check/play_store/product_purchases/product_verification.rb @@ -37,7 +37,7 @@ def call! private def valid? - @response[:result] && @response[:result].purchase_state && @response[:result].consumption_state + @response[:result]&.purchase_state && @response[:result]&.consumption_state end def verify! diff --git a/lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb b/lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb index 9d1068d..7ad89a3 100644 --- a/lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb +++ b/lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb @@ -5,7 +5,8 @@ module SubscriptionPurchases class SubscriptionPurchase include Utils::AttributeReader - # @return [Google::Apis::AndroidpublisherV3::SubscriptionPurchase] the raw subscription purchase from google-api-client + # @return [Google::Apis::AndroidpublisherV3::SubscriptionPurchase] the raw subscription purchase + # from google-api-client attr_reader :subscription_purchase # The payment of the subscription is pending (paymentState) diff --git a/lib/candy_check/play_store/subscription_purchases/subscription_verification.rb b/lib/candy_check/play_store/subscription_purchases/subscription_verification.rb index 1d6eea1..48a1ba7 100644 --- a/lib/candy_check/play_store/subscription_purchases/subscription_verification.rb +++ b/lib/candy_check/play_store/subscription_purchases/subscription_verification.rb @@ -38,6 +38,7 @@ def call! def valid? return false unless @response[:result] + ok_kind = @response[:result].kind == "androidpublisher#subscriptionPurchase" @response && @response[:result].expiry_time_millis && ok_kind end diff --git a/lib/candy_check/play_store/verification_failure.rb b/lib/candy_check/play_store/verification_failure.rb index 181b85d..3ea9da6 100644 --- a/lib/candy_check/play_store/verification_failure.rb +++ b/lib/candy_check/play_store/verification_failure.rb @@ -18,7 +18,7 @@ def initialize(error) # @return [Integer] def code Integer(error.status_code) - rescue + rescue StandardError -1 end diff --git a/lib/candy_check/utils.rb b/lib/candy_check/utils.rb index f14f370..c15e955 100644 --- a/lib/candy_check/utils.rb +++ b/lib/candy_check/utils.rb @@ -1,2 +1,2 @@ -require 'candy_check/utils/attribute_reader' -require 'candy_check/utils/config' +require "candy_check/utils/attribute_reader" +require "candy_check/utils/config" diff --git a/lib/candy_check/utils/attribute_reader.rb b/lib/candy_check/utils/attribute_reader.rb index ac5de3a..fefc5f3 100644 --- a/lib/candy_check/utils/attribute_reader.rb +++ b/lib/candy_check/utils/attribute_reader.rb @@ -1,4 +1,4 @@ -require 'date' +require "date" module CandyCheck module Utils @@ -23,7 +23,8 @@ def read_integer(field) def read_bool(field) val = read(field).to_s return nil unless %w(false true).include?(val) - val == 'true' + + val == "true" end def read_datetime_from_string(field) diff --git a/lib/candy_check/utils/config.rb b/lib/candy_check/utils/config.rb index 09e8794..935db6c 100644 --- a/lib/candy_check/utils/config.rb +++ b/lib/candy_check/utils/config.rb @@ -26,6 +26,7 @@ def validate! # @raise [ArgumentError] if attribute is missing def validates_presence(name) return if send(name) + raise ArgumentError, "Configuration field #{name} is missing" end @@ -34,6 +35,7 @@ def validates_presence(name) # @param values [Array] of possible values def validates_inclusion(name, *values) return if values.include?(send(name)) + raise ArgumentError, "Configuration field #{name} should be "\ "one of: #{values.join(', ')}" end diff --git a/spec/app_store/client_spec.rb b/spec/app_store/client_spec.rb index 265d9fa..acc531c 100644 --- a/spec/app_store/client_spec.rb +++ b/spec/app_store/client_spec.rb @@ -1,12 +1,12 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::Client do - let(:endpoint_url) { 'https://some.endpoint.com/verify' } + let(:endpoint_url) { "https://some.endpoint.com/verify" } let(:receipt_data) do - 'some_very_long_receipt_information_which_is_normaly_base64_encoded' + "some_very_long_receipt_information_which_is_normaly_base64_encoded" end let(:password) do - 'some_secret_password' + "some_secret_password" end let(:response) do '{ @@ -18,40 +18,40 @@ end let(:expected) do { - 'status' => 0, - 'receipt' => { - 'item_id' => '521129812' - } + "status" => 0, + "receipt" => { + "item_id" => "521129812", + }, } end subject { CandyCheck::AppStore::Client.new(endpoint_url) } - describe 'valid response' do - it 'sends JSON and parses the JSON response without a secret' do + describe "valid response" do + it "sends JSON and parses the JSON response without a secret" do stub_endpoint .with( body: { - 'receipt-data' => receipt_data - } + "receipt-data" => receipt_data, + }, ) .to_return( - body: response + body: response, ) result = subject.verify(receipt_data) _(result).must_equal expected end - it 'sends JSON and parses the JSON response with a secret' do + it "sends JSON and parses the JSON response with a secret" do stub_endpoint .with( body: { - 'receipt-data' => receipt_data, - 'password' => password - } + "receipt-data" => receipt_data, + "password" => password, + }, ) .to_return( - body: response + body: response, ) result = subject.verify(receipt_data, password) _(result).must_equal expected diff --git a/spec/app_store/config_spec.rb b/spec/app_store/config_spec.rb index 42549f9..433d431 100644 --- a/spec/app_store/config_spec.rb +++ b/spec/app_store/config_spec.rb @@ -1,39 +1,39 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::Config do subject { CandyCheck::AppStore::Config.new(attributes) } - describe 'valid' do + describe "valid" do let(:attributes) do { - environment: :sandbox + environment: :sandbox, } end - it 'returns environment' do + it "returns environment" do _(subject.environment).must_equal :sandbox end - it 'checks for production?' do + it "checks for production?" do _(subject.production?).must_be_false other = CandyCheck::AppStore::Config.new( - environment: :production + environment: :production, ) _(other.production?).must_be_true end end - describe 'invalid' do + describe "invalid" do let(:attributes) do {} end - it 'needs an environment' do + it "needs an environment" do _(proc { subject }).must_raise ArgumentError end - it 'needs an included environment' do + it "needs an included environment" do attributes[:environment] = :invalid _(proc { subject }).must_raise ArgumentError end diff --git a/spec/app_store/receipt_collection_spec.rb b/spec/app_store/receipt_collection_spec.rb index 624a3ea..e10c5ea 100644 --- a/spec/app_store/receipt_collection_spec.rb +++ b/spec/app_store/receipt_collection_spec.rb @@ -1,98 +1,96 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::ReceiptCollection do subject { CandyCheck::AppStore::ReceiptCollection.new(attributes) } - describe 'overdue subscription' do + describe "overdue subscription" do let(:attributes) do [{ - 'expires_date' => '2014-04-15 12:52:40 Etc/GMT', - 'expires_date_pst' => '2014-04-15 05:52:40 America/Los_Angeles', - 'purchase_date' => '2014-04-14 12:52:40 Etc/GMT', - 'is_trial_period' => 'false' + "expires_date" => "2014-04-15 12:52:40 Etc/GMT", + "expires_date_pst" => "2014-04-15 05:52:40 America/Los_Angeles", + "purchase_date" => "2014-04-14 12:52:40 Etc/GMT", + "is_trial_period" => "false", }, { - 'expires_date' => '2015-04-15 12:52:40 Etc/GMT', - 'expires_date_pst' => '2015-04-15 05:52:40 America/Los_Angeles', - 'purchase_date' => '2015-04-14 12:52:40 Etc/GMT', - 'is_trial_period' => 'false' + "expires_date" => "2015-04-15 12:52:40 Etc/GMT", + "expires_date_pst" => "2015-04-15 05:52:40 America/Los_Angeles", + "purchase_date" => "2015-04-14 12:52:40 Etc/GMT", + "is_trial_period" => "false", }] end - it 'is expired' do + it "is expired" do _(subject.expired?).must_be_true end - it 'is not a trial' do + it "is not a trial" do _(subject.trial?).must_be_false end - it 'has positive overdue days' do + it "has positive overdue days" do overdue = subject.overdue_days _(overdue).must_be_instance_of Integer assert overdue > 0 end - it 'has a last expires date' do + it "has a last expires date" do expected = DateTime.new(2015, 4, 15, 12, 52, 40) _(subject.expires_at).must_equal expected end - it 'is expired? at same pointin time' do + it "is expired? at same pointin time" do Timecop.freeze(Time.utc(2015, 4, 15, 12, 52, 40)) do _(subject.expired?).must_be_true end end end - describe 'unordered receipts' do + describe "unordered receipts" do let(:attributes) do [{ - 'expires_date' => '2015-04-15 12:52:40 Etc/GMT', - 'expires_date_pst' => '2015-04-15 05:52:40 America/Los_Angeles', - 'purchase_date' => '2015-04-14 12:52:40 Etc/GMT', - 'is_trial_period' => 'false' - }, { - 'expires_date' => '2014-04-15 12:52:40 Etc/GMT', - 'expires_date_pst' => '2014-04-15 05:52:40 America/Los_Angeles', - 'purchase_date' => '2014-04-14 12:52:40 Etc/GMT', - 'is_trial_period' => 'false' - }] + "expires_date" => "2015-04-15 12:52:40 Etc/GMT", + "expires_date_pst" => "2015-04-15 05:52:40 America/Los_Angeles", + "purchase_date" => "2015-04-14 12:52:40 Etc/GMT", + "is_trial_period" => "false", + }, { + "expires_date" => "2014-04-15 12:52:40 Etc/GMT", + "expires_date_pst" => "2014-04-15 05:52:40 America/Los_Angeles", + "purchase_date" => "2014-04-14 12:52:40 Etc/GMT", + "is_trial_period" => "false", + }] end - it 'the expires date is the latest one in time' do + it "the expires date is the latest one in time" do expected = DateTime.new(2015, 4, 15, 12, 52, 40) _(subject.expires_at).must_equal expected end - end - describe 'unexpired trial subscription' do + describe "unexpired trial subscription" do two_days_from_now = DateTime.now + 2 let(:attributes) do [{ - 'expires_date' => '2016-04-15 12:52:40 Etc/GMT', - 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT', - 'is_trial_period' => 'true' + "expires_date" => "2016-04-15 12:52:40 Etc/GMT", + "purchase_date" => "2016-04-15 12:52:40 Etc/GMT", + "is_trial_period" => "true", }, { - 'expires_date' => - two_days_from_now.strftime('%Y-%m-%d %H:%M:%S Etc/GMT'), - 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT', - 'is_trial_period' => 'true' + "expires_date" => + two_days_from_now.strftime("%Y-%m-%d %H:%M:%S Etc/GMT"), + "purchase_date" => "2016-04-15 12:52:40 Etc/GMT", + "is_trial_period" => "true", }] end - it 'has not expired' do + it "has not expired" do _(subject.expired?).must_be_false end - it 'it is a trial' do + it "it is a trial" do _(subject.trial?).must_be_true end - it 'expires in two days' do + it "expires in two days" do _(subject.overdue_days).must_equal(-2) end end - end diff --git a/spec/app_store/receipt_spec.rb b/spec/app_store/receipt_spec.rb index 7b974d2..56a93ce 100644 --- a/spec/app_store/receipt_spec.rb +++ b/spec/app_store/receipt_spec.rb @@ -1,105 +1,105 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::Receipt do subject { CandyCheck::AppStore::Receipt.new(attributes) } let(:attributes) do { - 'original_purchase_date_pst' => '2015-01-08 03:40:46' \ - ' America/Los_Angeles', - 'purchase_date_ms' => '1420803646868', - 'unique_identifier' => 'some_uniq_identifier_from_apple' \ - '_for_this', - 'original_transaction_id' => 'some_original_transaction_id', - 'bvrs' => '2.0', - 'transaction_id' => 'some_transaction_id', - 'quantity' => '1', - 'unique_vendor_identifier' => '00000000-1111-2222-3333-' \ - '444444444444', - 'item_id' => 'some_item_id', - 'product_id' => 'some_product', - 'purchase_date' => '2015-01-09 11:40:46 Etc/GMT', - 'original_purchase_date' => '2015-01-08 11:40:46 Etc/GMT', - 'purchase_date_pst' => '2015-01-09 03:40:46' \ - ' America/Los_Angeles', - 'bid' => 'some.test.app', - 'original_purchase_date_ms' => '1420717246868', - 'expires_date' => '2016-06-09 13:59:40 Etc/GMT', - 'is_trial_period' => 'false' + "original_purchase_date_pst" => "2015-01-08 03:40:46" \ + " America/Los_Angeles", + "purchase_date_ms" => "1420803646868", + "unique_identifier" => "some_uniq_identifier_from_apple" \ + "_for_this", + "original_transaction_id" => "some_original_transaction_id", + "bvrs" => "2.0", + "transaction_id" => "some_transaction_id", + "quantity" => "1", + "unique_vendor_identifier" => "00000000-1111-2222-3333-" \ + "444444444444", + "item_id" => "some_item_id", + "product_id" => "some_product", + "purchase_date" => "2015-01-09 11:40:46 Etc/GMT", + "original_purchase_date" => "2015-01-08 11:40:46 Etc/GMT", + "purchase_date_pst" => "2015-01-09 03:40:46" \ + " America/Los_Angeles", + "bid" => "some.test.app", + "original_purchase_date_ms" => "1420717246868", + "expires_date" => "2016-06-09 13:59:40 Etc/GMT", + "is_trial_period" => "false", } end - describe 'valid transaction' do - it 'is valid' do + describe "valid transaction" do + it "is valid" do _(subject.valid?).must_be_true end - it 'returns the item\'s id' do - _(subject.item_id).must_equal 'some_item_id' + it "returns the item's id" do + _(subject.item_id).must_equal "some_item_id" end - it 'returns the item\'s product_id' do - _(subject.product_id).must_equal 'some_product' + it "returns the item's product_id" do + _(subject.product_id).must_equal "some_product" end - it 'returns the quantity' do + it "returns the quantity" do _(subject.quantity).must_equal 1 end - it 'returns the app version' do - _(subject.app_version).must_equal '2.0' + it "returns the app version" do + _(subject.app_version).must_equal "2.0" end - it 'returns the bundle identifier' do - _(subject.bundle_identifier).must_equal 'some.test.app' + it "returns the bundle identifier" do + _(subject.bundle_identifier).must_equal "some.test.app" end - it 'returns the purchase date' do + it "returns the purchase date" do expected = DateTime.new(2015, 1, 9, 11, 40, 46) _(subject.purchase_date).must_equal expected end - it 'returns the original purchase date' do + it "returns the original purchase date" do expected = DateTime.new(2015, 1, 8, 11, 40, 46) _(subject.original_purchase_date).must_equal expected end - it 'returns the transaction id' do - _(subject.transaction_id).must_equal 'some_transaction_id' + it "returns the transaction id" do + _(subject.transaction_id).must_equal "some_transaction_id" end - it 'returns the original transaction id' do - _(subject.original_transaction_id).must_equal 'some_original_transaction_id' + it "returns the original transaction id" do + _(subject.original_transaction_id).must_equal "some_original_transaction_id" end - it 'return nil for cancellation date' do + it "return nil for cancellation date" do _(subject.cancellation_date).must_be_nil end - it 'returns raw attributes' do + it "returns raw attributes" do _(subject.attributes).must_be_same_as attributes end - it 'returns the subscription expiration date' do + it "returns the subscription expiration date" do expected = DateTime.new(2016, 6, 9, 13, 59, 40) _(subject.expires_date).must_equal expected end - it 'returns the trial status' do + it "returns the trial status" do _(subject.is_trial_period).must_be_false end end - describe 'valid transaction' do + describe "valid transaction" do before do - attributes['cancellation_date'] = '2015-01-12 11:40:46 Etc/GMT' + attributes["cancellation_date"] = "2015-01-12 11:40:46 Etc/GMT" end - it 'isn\'t valid' do + it "isn't valid" do _(subject.valid?).must_be_false end - it 'return nil for cancellation date' do + it "return nil for cancellation date" do expected = DateTime.new(2015, 1, 12, 11, 40, 46) _(subject.cancellation_date).must_equal expected end diff --git a/spec/app_store/subscription_verification_spec.rb b/spec/app_store/subscription_verification_spec.rb index 0e2b250..a848542 100644 --- a/spec/app_store/subscription_verification_spec.rb +++ b/spec/app_store/subscription_verification_spec.rb @@ -1,15 +1,15 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::SubscriptionVerification do subject do CandyCheck::AppStore::SubscriptionVerification.new(endpoint, data, secret) end - let(:endpoint) { 'https://some.endpoint' } - let(:data) { 'some_data' } - let(:secret) { 'some_secret' } + let(:endpoint) { "https://some.endpoint" } + let(:data) { "some_data" } + let(:secret) { "some_secret" } - it 'returns a verification failure for status != 0' do - with_mocked_response('status' => 21_000) do |client, recorded| + it "returns a verification failure for status != 0" do + with_mocked_response("status" => 21_000) do |client, recorded| result = subject.call! _(client.receipt_data).must_equal data _(client.secret).must_equal secret @@ -21,7 +21,7 @@ end end - it 'returns a verification failure when receipt is missing' do + it "returns a verification failure when receipt is missing" do with_mocked_response({}) do |client, recorded| result = subject.call! _(client.receipt_data).must_equal data @@ -34,13 +34,13 @@ end end - it 'returns a collection of receipt when status is 0 and receipts exists' do + it "returns a collection of receipt when status is 0 and receipts exists" do response = { - 'status' => 0, - 'latest_receipt_info' => [ - { 'item_id' => 'some_id', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' }, - { 'item_id' => 'some_other_id', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' } - ] + "status" => 0, + "latest_receipt_info" => [ + { "item_id" => "some_id", "purchase_date" => "2016-04-15 12:52:40 Etc/GMT" }, + { "item_id" => "some_other_id", "purchase_date" => "2016-04-15 12:52:40 Etc/GMT" }, + ], } with_mocked_response(response) do result = subject.call! @@ -49,29 +49,30 @@ _(result.receipts.size).must_equal(2) last = result.receipts.last _(last).must_be_instance_of CandyCheck::AppStore::Receipt - _(last.item_id).must_equal('some_other_id') + _(last.item_id).must_equal("some_other_id") end end - describe 'filtered product_ids' do + describe "filtered product_ids" do subject do CandyCheck::AppStore::SubscriptionVerification.new( endpoint, data, secret, - product_ids + product_ids, ) end - let(:product_ids) { ['product_1'] } + let(:product_ids) { ["product_1"] } - it 'returns only filtered reciepts when specifc product_ids are reqested' do + it "returns only filtered reciepts when specifc product_ids are reqested" do response = { - 'status' => 0, - 'latest_receipt_info' => [ - { 'item_id' => 'some_id', 'product_id' => 'product_1', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' }, - { 'item_id' => 'some_other_id', 'product_id' => 'product_1', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' }, - { 'item_id' => 'some_id', 'product_id' => 'product_2', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' } - ] + "status" => 0, + "latest_receipt_info" => [ + { "item_id" => "some_id", "product_id" => "product_1", "purchase_date" => "2016-04-15 12:52:40 Etc/GMT" }, + { "item_id" => "some_other_id", "product_id" => "product_1", + "purchase_date" => "2016-04-15 12:52:40 Etc/GMT" }, + { "item_id" => "some_id", "product_id" => "product_2", "purchase_date" => "2016-04-15 12:52:40 Etc/GMT" }, + ], } with_mocked_response(response) do result = subject.call! @@ -80,26 +81,28 @@ _(result.receipts.size).must_equal(2) last = result.receipts.last _(last).must_be_instance_of CandyCheck::AppStore::Receipt - _(last.item_id).must_equal('some_other_id') + _(last.item_id).must_equal("some_other_id") end end end private - DummyClient = Struct.new(:response) do - attr_reader :receipt_data, :secret + let(:dummy_client_class) do + Struct.new(:response) do + attr_reader :receipt_data, :secret - def verify(receipt_data, secret) - @receipt_data = receipt_data - @secret = secret - response + def verify(receipt_data, secret) + @receipt_data = receipt_data + @secret = secret + response + end end end def with_mocked_response(response) recorded = [] - dummy = DummyClient.new(response) + dummy = dummy_client_class.new(response) stub = proc do |*args| recorded << args dummy diff --git a/spec/app_store/verifcation_failure_spec.rb b/spec/app_store/verifcation_failure_spec.rb index ef17db1..af640df 100644 --- a/spec/app_store/verifcation_failure_spec.rb +++ b/spec/app_store/verifcation_failure_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::VerificationFailure do subject { CandyCheck::AppStore::VerificationFailure } @@ -6,23 +6,23 @@ [21_000, 21_002, 21_003, 21_004, 21_005, 21_006, 21_007, 21_008] end - it 'fetched an failure with message for every known code' do + it "fetched an failure with message for every known code" do known.each do |code| got = subject.fetch(code) _(got.code).must_equal code - _(got.message).wont_equal 'Unknown error' + _(got.message).wont_equal "Unknown error" end end - it 'fetched an failure for unknown codes' do + it "fetched an failure for unknown codes" do got = subject.fetch(1234) _(got.code).must_equal 1234 - _(got.message).must_equal 'Unknown error' + _(got.message).must_equal "Unknown error" end - it 'fetched an failure for nil code' do + it "fetched an failure for nil code" do got = subject.fetch(nil) _(got.code).must_equal(-1) - _(got.message).must_equal 'Unknown error' + _(got.message).must_equal "Unknown error" end end diff --git a/spec/app_store/verification_spec.rb b/spec/app_store/verification_spec.rb index 6fdac6f..58b0f06 100644 --- a/spec/app_store/verification_spec.rb +++ b/spec/app_store/verification_spec.rb @@ -1,13 +1,13 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::Verification do subject { CandyCheck::AppStore::Verification.new(endpoint, data, secret) } - let(:endpoint) { 'https://some.endpoint' } - let(:data) { 'some_data' } - let(:secret) { 'some_secret' } + let(:endpoint) { "https://some.endpoint" } + let(:data) { "some_data" } + let(:secret) { "some_secret" } - it 'returns a verification failure for status != 0' do - with_mocked_response('status' => 21_000) do |client, recorded| + it "returns a verification failure for status != 0" do + with_mocked_response("status" => 21_000) do |client, recorded| result = subject.call! _(client.receipt_data).must_equal data _(client.secret).must_equal secret @@ -19,7 +19,7 @@ end end - it 'returns a verification failure when receipt is missing' do + it "returns a verification failure when receipt is missing" do with_mocked_response({}) do |client, recorded| result = subject.call! _(client.receipt_data).must_equal data @@ -32,20 +32,32 @@ end end - it 'returns a receipt when status is 0 and receipt exists' do - response = { 'status' => 0, 'receipt' => { 'item_id' => 'some_id' } } + it "returns a receipt when status is 0 and receipt exists" do + response = { "status" => 0, "receipt" => { "item_id" => "some_id" } } with_mocked_response(response) do result = subject.call! _(result).must_be_instance_of CandyCheck::AppStore::Receipt - _(result.item_id).must_equal('some_id') + _(result.item_id).must_equal("some_id") end end private + let(:dummy_client_class) do + Struct.new(:response) do + attr_reader :receipt_data, :secret + + def verify(receipt_data, secret) + @receipt_data = receipt_data + @secret = secret + response + end + end + end + def with_mocked_response(response) recorded = [] - dummy = DummyClient.new(response) + dummy = dummy_client_class.new(response) stub = proc do |*args| recorded << args dummy diff --git a/spec/app_store/verifier_spec.rb b/spec/app_store/verifier_spec.rb index f96eb10..140fbb1 100644 --- a/spec/app_store/verifier_spec.rb +++ b/spec/app_store/verifier_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::AppStore::Verifier do subject { CandyCheck::AppStore::Verifier.new(config) } @@ -6,32 +6,32 @@ CandyCheck::AppStore::Config.new(environment: environment) end let(:environment) { :production } - let(:data) { 'some_data' } - let(:secret) { 'some_secret' } + let(:data) { "some_data" } + let(:secret) { "some_secret" } let(:receipt) { CandyCheck::AppStore::Receipt.new({}) } let(:receipt_collection) { CandyCheck::AppStore::ReceiptCollection.new({}) } let(:production_endpoint) do - 'https://buy.itunes.apple.com/verifyReceipt' + "https://buy.itunes.apple.com/verifyReceipt" end let(:sandbox_endpoint) do - 'https://sandbox.itunes.apple.com/verifyReceipt' + "https://sandbox.itunes.apple.com/verifyReceipt" end - it 'holds the config' do + it "holds the config" do _(subject.config).must_be_same_as config end - describe 'sandbox' do + describe "sandbox" do let(:environment) { :sandbox } - it 'uses sandbox endpoint without retry on success' do + it "uses sandbox endpoint without retry on success" do with_mocked_verifier(receipt) do _(subject.verify(data, secret)).must_be_same_as receipt assert_recorded([sandbox_endpoint, data, secret]) end end - it 'only uses sandbox endpoint for normal failures' do + it "only uses sandbox endpoint for normal failures" do failure = get_failure(21_000) with_mocked_verifier(failure) do _(subject.verify(data, secret)).must_be_same_as failure @@ -39,29 +39,29 @@ end end - it 'retries production endpoint for redirect error' do + it "retries production endpoint for redirect error" do failure = get_failure(21_008) with_mocked_verifier(failure, receipt) do _(subject.verify(data, secret)).must_be_same_as receipt assert_recorded( [sandbox_endpoint, data, secret], - [production_endpoint, data, secret] + [production_endpoint, data, secret], ) end end end - describe 'production' do + describe "production" do let(:environment) { :production } - it 'uses production endpoint without retry on success' do + it "uses production endpoint without retry on success" do with_mocked_verifier(receipt) do _(subject.verify(data, secret)).must_be_same_as receipt assert_recorded([production_endpoint, data, secret]) end end - it 'only uses production endpoint for normal failures' do + it "only uses production endpoint for normal failures" do failure = get_failure(21_000) with_mocked_verifier(failure) do _(subject.verify(data, secret)).must_be_same_as failure @@ -69,31 +69,31 @@ end end - it 'retries production endpoint for redirect error' do + it "retries production endpoint for redirect error" do failure = get_failure(21_007) with_mocked_verifier(failure, receipt) do _(subject.verify(data, secret)).must_be_same_as receipt assert_recorded( [production_endpoint, data, secret], - [sandbox_endpoint, data, secret] + [sandbox_endpoint, data, secret], ) end end end - describe 'subscription' do + describe "subscription" do let(:environment) { :production } - it 'uses production endpoint without retry on success' do + it "uses production endpoint without retry on success" do with_mocked_verifier(receipt_collection) do _(subject.verify_subscription( - data, secret - )).must_be_same_as receipt_collection + data, secret + )).must_be_same_as receipt_collection assert_recorded([production_endpoint, data, secret, nil]) end end - it 'only uses production endpoint for normal failures' do + it "only uses production endpoint for normal failures" do failure = get_failure(21_000) with_mocked_verifier(failure) do _(subject.verify_subscription(data, secret)).must_be_same_as failure @@ -101,23 +101,23 @@ end end - it 'retries production endpoint for redirect error' do + it "retries production endpoint for redirect error" do failure = get_failure(21_007) with_mocked_verifier(failure, receipt) do _(subject.verify_subscription(data, secret)).must_be_same_as receipt assert_recorded( [production_endpoint, data, secret, nil], - [sandbox_endpoint, data, secret, nil] + [sandbox_endpoint, data, secret, nil], ) end end - it 'passed the product_ids' do - product_ids = ['product_1'] + it "passed the product_ids" do + product_ids = ["product_1"] with_mocked_verifier(receipt_collection) do _(subject.verify_subscription( - data, secret, product_ids - )).must_be_same_as receipt_collection + data, secret, product_ids + )).must_be_same_as receipt_collection assert_recorded([production_endpoint, data, secret, product_ids]) end end @@ -125,15 +125,23 @@ private - def with_mocked_verifier(*results) + let(:dummy_verification_class) do + Struct.new(:endpoint, :data, :secret, :product_ids) do + attr_accessor :results + + def call! + results.shift + end + end + end + + def with_mocked_verifier(*results, &block) @recorded ||= [] stub = proc do |*args| @recorded << args - DummyAppStoreVerification.new(*args).tap { |v| v.results = results } - end - CandyCheck::AppStore::Verification.stub :new, stub do - yield + dummy_verification_class.new(*args).tap { |v| v.results = results } end + CandyCheck::AppStore::Verification.stub :new, stub, &block end def assert_recorded(*calls) @@ -143,20 +151,4 @@ def assert_recorded(*calls) def get_failure(code) CandyCheck::AppStore::VerificationFailure.fetch(code) end - - class DummyAppStoreVerification - attr_reader :endpoint, :data, :secret, :product_ids - attr_accessor :results - - def initialize(endpoint, data, secret, product_ids = nil) - @endpoint = endpoint - @data = data - @secret = secret - @product_ids = product_ids - end - - def call! - results.shift - end - end end diff --git a/spec/cli/app_spec.rb b/spec/cli/app_spec.rb index 513d9b8..23718be 100644 --- a/spec/cli/app_spec.rb +++ b/spec/cli/app_spec.rb @@ -1,42 +1,40 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::CLI::App do subject { CandyCheck::CLI::App.new } - it 'supports the version command' do + it "supports the version command" do stub_command(CandyCheck::CLI::Commands::Version) do _(subject.version).must_equal :stubbed _(@arguments).must_be_empty end end - it 'supports the app_store command' do + it "supports the app_store command" do stub_command(CandyCheck::CLI::Commands::AppStore) do - _(subject.app_store('receipt')).must_equal :stubbed - _(@arguments).must_equal ['receipt'] + _(subject.app_store("receipt")).must_equal :stubbed + _(@arguments).must_equal ["receipt"] end end - it 'supports the play_store command' do + it "supports the play_store command" do stub_command(CandyCheck::CLI::Commands::PlayStore) do - _(subject.play_store('package', 'id', 'token')).must_equal :stubbed - _(@arguments).must_equal ['package', 'id', 'token'] + _(subject.play_store("package", "id", "token")).must_equal :stubbed + _(@arguments).must_equal %w(package id token) end end - it 'returns true when call .exit_on_failure?' do + it "returns true when call .exit_on_failure?" do _(CandyCheck::CLI::App.exit_on_failure?).must_equal true end private - def stub_command(target) + def stub_command(target, &block) stub = proc do |*args| @arguments = args :stubbed end - target.stub :run, stub do - yield - end + target.stub :run, stub, &block end end diff --git a/spec/cli/commands/app_store_spec.rb b/spec/cli/commands/app_store_spec.rb index 24bd774..857c494 100644 --- a/spec/cli/commands/app_store_spec.rb +++ b/spec/cli/commands/app_store_spec.rb @@ -1,56 +1,55 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::CLI::Commands::AppStore do include WithCommand subject { CandyCheck::CLI::Commands::AppStore } let(:arguments) { [receipt, options] } - let(:receipt) { 'data' } + let(:receipt) { "data" } let(:options) do { - environment: :sandbox + environment: :sandbox, } end + let(:dummy_verifier_class) do + Struct.new(:config) do + attr_reader :arguments + + def verify(*arguments) + @arguments = arguments + { result: :stubbed } + end + end + end before do stub = proc do |*args| - @verifier = DummyAppStoreVerifier.new(*args) + @verifier = dummy_verifier_class.new(*args) end CandyCheck::AppStore::Verifier.stub :new, stub do run_command! end end - describe 'default' do - it 'uses the receipt and the options' do + describe "default" do + it "uses the receipt and the options" do _(@verifier.config.environment).must_equal :sandbox _(@verifier.arguments).must_equal [receipt, nil] - _(out.lines).must_equal ['Hash:', { result: :stubbed }] + _(out.lines).must_equal ["Hash:", { result: :stubbed }] end end - describe 'with secret' do + describe "with secret" do let(:options) do { environment: :production, - secret: 'notasecret' + secret: "notasecret", } end - it 'uses the secret for verification' do + it "uses the secret for verification" do _(@verifier.config.environment).must_equal :production - _(@verifier.arguments).must_equal [receipt, 'notasecret'] - _(out.lines).must_equal ['Hash:', { result: :stubbed }] - end - end - - private - - DummyAppStoreVerifier = Struct.new(:config) do - attr_reader :arguments - - def verify(*arguments) - @arguments = arguments - { result: :stubbed } + _(@verifier.arguments).must_equal [receipt, "notasecret"] + _(out.lines).must_equal ["Hash:", { result: :stubbed }] end end end diff --git a/spec/cli/commands/play_store_spec.rb b/spec/cli/commands/play_store_spec.rb index 5a7013a..05da8cd 100644 --- a/spec/cli/commands/play_store_spec.rb +++ b/spec/cli/commands/play_store_spec.rb @@ -2,7 +2,9 @@ describe CandyCheck::CLI::Commands::PlayStore do include WithCommand - subject { CandyCheck::CLI::Commands::PlayStore.new(package_name, product_id, token, "json_key_file" => json_key_file) } + subject do + CandyCheck::CLI::Commands::PlayStore.new(package_name, product_id, token, "json_key_file" => json_key_file) + end let(:package_name) { "my_package_name" } let(:product_id) { "my_product_id" } let(:token) { "my_token" } diff --git a/spec/cli/commands/version_spec.rb b/spec/cli/commands/version_spec.rb index 10760b0..26e5cd1 100644 --- a/spec/cli/commands/version_spec.rb +++ b/spec/cli/commands/version_spec.rb @@ -1,10 +1,10 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::CLI::Commands::Version do include WithCommand subject { CandyCheck::CLI::Commands::Version } - it 'prints the gem\'s version' do + it "prints the gem's version" do run_command! _(out.lines).must_equal [CandyCheck::VERSION] end diff --git a/spec/cli/out_spec.rb b/spec/cli/out_spec.rb index 7a3a160..a04bbc2 100644 --- a/spec/cli/out_spec.rb +++ b/spec/cli/out_spec.rb @@ -1,34 +1,34 @@ -require 'spec_helper' +require "spec_helper" describe CandyCheck::CLI::Out do subject { CandyCheck::CLI::Out.new(out) } let(:out) { StringIO.new } - it 'defaults to use STDOUT' do + it "defaults to use STDOUT" do _(CandyCheck::CLI::Out.new.out).must_be_same_as $stdout end - it 'holds the outlet' do + it "holds the outlet" do _(subject.out).must_be_same_as out end - it 'prints to outlet' do - subject.print 'some text' - subject.print 'another line' + it "prints to outlet" do + subject.print "some text" + subject.print "another line" close _(out.readlines).must_equal [ "some text\n", - "another line\n" + "another line\n", ] end - it 'pretty prints to outlet' do + it "pretty prints to outlet" do subject.pretty dummy: 1 subject.pretty [1, 2, 3] close _(out.readlines).must_equal [ "{:dummy=>1}\n", - "[1, 2, 3]\n" + "[1, 2, 3]\n", ] end diff --git a/spec/play_store/acknowledger_spec.rb b/spec/play_store/acknowledger_spec.rb index 0fbb1e2..1f1247d 100644 --- a/spec/play_store/acknowledger_spec.rb +++ b/spec/play_store/acknowledger_spec.rb @@ -21,7 +21,9 @@ end end it "when already acknowledged" do + # rubocop:disable Layout/LineLength error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"errors\": [\n {\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"invalidPurchaseState\",\n \"location\": \"token\",\n \"locationType\": \"parameter\"\n }\n ]\n }\n}\n" + # rubocop:enable Layout/LineLength VCR.use_cassette("play_store/product_acknowledgements/already_acknowledged") do result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token) @@ -33,7 +35,9 @@ end end it "when it has been refunded" do + # rubocop:disable Layout/LineLength error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The product purchase is not owned by the user.\",\n \"errors\": [\n {\n \"message\": \"The product purchase is not owned by the user.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"productNotOwnedByUser\"\n }\n ]\n }\n}\n" + # rubocop:enable Layout/LineLength VCR.use_cassette("play_store/product_acknowledgements/refunded") do result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token) diff --git a/spec/play_store/product_acknowledgements/acknowledgement_spec.rb b/spec/play_store/product_acknowledgements/acknowledgement_spec.rb index b7c1d86..c5b15b2 100644 --- a/spec/play_store/product_acknowledgements/acknowledgement_spec.rb +++ b/spec/play_store/product_acknowledgements/acknowledgement_spec.rb @@ -27,7 +27,9 @@ end end it "when already acknowledged" do + # rubocop:disable Layout/LineLength error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"errors\": [\n {\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"invalidPurchaseState\",\n \"location\": \"token\",\n \"locationType\": \"parameter\"\n }\n ]\n }\n}\n" + # rubocop:enable Layout/LineLength VCR.use_cassette("play_store/product_acknowledgements/already_acknowledged") do result = subject.call! @@ -39,7 +41,9 @@ end end it "when it has been refunded" do + # rubocop:disable Layout/LineLength error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The product purchase is not owned by the user.\",\n \"errors\": [\n {\n \"message\": \"The product purchase is not owned by the user.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"productNotOwnedByUser\"\n }\n ]\n }\n}\n" + # rubocop:enable Layout/LineLength VCR.use_cassette("play_store/product_acknowledgements/refunded") do result = subject.call! diff --git a/spec/play_store/product_acknowledgements/response_spec.rb b/spec/play_store/product_acknowledgements/response_spec.rb index 99e05f2..e029a68 100644 --- a/spec/play_store/product_acknowledgements/response_spec.rb +++ b/spec/play_store/product_acknowledgements/response_spec.rb @@ -5,23 +5,23 @@ CandyCheck::PlayStore::ProductAcknowledgements::Response.new(result: result, error_data: error_data) end - describe '#acknowledged?' do - context 'when result present' do - let(:result) { '' } + describe "#acknowledged?" do + context "when result present" do + let(:result) { "" } let(:error_data) { nil } - it 'returns true' do + it "returns true" do result = subject.acknowledged? _(result).must_be_true end end - context 'when result is not present' do + context "when result is not present" do let(:result) { nil } - let(:error_data) { nil } + let(:error_data) { nil } - it 'returns false' do + it "returns false" do result = subject.acknowledged? _(result).must_be_false @@ -29,34 +29,35 @@ end end - describe '#error' do - context 'when error present' do + describe "#error" do + context "when error present" do let(:result) { nil } let(:error_data) do Module.new do def status_code 400 end + def body - 'A String describing the issue' + "A String describing the issue" end module_function :status_code, :body end end - it 'returns the expected data' do + it "returns the expected data" do result = subject.error _(result[:status_code]).must_equal(400) - _(result[:body]).must_equal('A String describing the issue') + _(result[:body]).must_equal("A String describing the issue") end end - context 'when error is not present' do - let(:result) { '' } + context "when error is not present" do + let(:result) { "" } let(:error_data) { nil } - it 'returns false' do + it "returns false" do result = subject.error _(result).must_be_nil diff --git a/spec/play_store/product_purchases/product_purchase_spec.rb b/spec/play_store/product_purchases/product_purchase_spec.rb index 2416dc1..092a2c6 100644 --- a/spec/play_store/product_purchases/product_purchase_spec.rb +++ b/spec/play_store/product_purchases/product_purchase_spec.rb @@ -5,13 +5,13 @@ describe "valid and non-consumed product" do let(:fake_product_purchase) do - FakeProductPurchase.new( + OpenStruct.new( consumption_state: 0, developer_payload: "payload that gets stored and returned", kind: "androidpublisher#productPurchase", order_id: "ABC123", purchase_state: 0, - purchase_time_millis: 1421676237413, + purchase_time_millis: 1_421_676_237_413, ) end @@ -51,13 +51,13 @@ describe "valid and consumed product" do let(:fake_product_purchase) do - FakeProductPurchase.new( + OpenStruct.new( consumption_state: 1, developer_payload: "payload that gets stored and returned", kind: "androidpublisher#productPurchase", order_id: "ABC123", purchase_state: 0, - purchase_time_millis: 1421676237413, + purchase_time_millis: 1_421_676_237_413, ) end @@ -72,13 +72,13 @@ describe "non-valid product" do let(:fake_product_purchase) do - FakeProductPurchase.new( + OpenStruct.new( consumption_state: 0, developer_payload: "payload that gets stored and returned", kind: "androidpublisher#productPurchase", order_id: "ABC123", purchase_state: 1, - purchase_time_millis: 1421676237413, + purchase_time_millis: 1_421_676_237_413, ) end @@ -86,25 +86,4 @@ _(subject.valid?).must_be_false end end - - private - - class FakeProductPurchase - FIELDS = [ - :consumption_state, - :developer_payload, - :kind, - :order_id, - :purchase_state, - :purchase_time_millis, - ].freeze - - attr_accessor(*FIELDS) - - def initialize(hash) - FIELDS.each do |key| - self.public_send("#{key}=", hash[key]) - end - end - end end diff --git a/spec/play_store/subscription_purchases/subscription_purchase_spec.rb b/spec/play_store/subscription_purchases/subscription_purchase_spec.rb index 3880a49..6e8a8ac 100644 --- a/spec/play_store/subscription_purchases/subscription_purchase_spec.rb +++ b/spec/play_store/subscription_purchases/subscription_purchase_spec.rb @@ -5,10 +5,10 @@ describe "expired and canceled subscription" do let(:fake_subscription_purchase) do - FakeSubscriptionPurchase.new( + OpenStruct.new( kind: "androidpublisher#subscriptionPurchase", - start_time_millis: 1459540113244, - expiry_time_millis: 1462132088610, + start_time_millis: 1_459_540_113_244, + expiry_time_millis: 1_462_132_088_610, auto_renewing: false, developer_payload: "payload that gets stored and returned", cancel_reason: 0, @@ -66,10 +66,11 @@ describe "unexpired and renewing subscription" do two_days_from_now = DateTime.now + 2 + let(:fake_subscription_purchase) do - FakeSubscriptionPurchase.new( + OpenStruct.new( kind: "androidpublisher#subscriptionPurchase", - start_time_millis: 1459540113244, + start_time_millis: 1_459_540_113_244, expiry_time_millis: (two_days_from_now.to_time.to_i * 1000), auto_renewing: true, developer_payload: "payload that gets stored and returned", @@ -89,10 +90,10 @@ describe "expired due to payment failure" do let(:fake_subscription_purchase) do - FakeSubscriptionPurchase.new( + OpenStruct.new( kind: "androidpublisher#subscriptionPurchase", - start_time_millis: 1459540113244, - expiry_time_millis: 1462132088610, + start_time_millis: 1_459_540_113_244, + expiry_time_millis: 1_462_132_088_610, auto_renewing: true, developer_payload: "payload that gets stored and returned", cancel_reason: 1, @@ -112,10 +113,10 @@ describe "subscription cancelation by user" do describe "when subscription is not canceled" do let(:fake_subscription_purchase) do - FakeSubscriptionPurchase.new( + OpenStruct.new( kind: "androidpublisher#subscriptionPurchase", - start_time_millis: 1459540113244, - expiry_time_millis: 1462132088610, + start_time_millis: 1_459_540_113_244, + expiry_time_millis: 1_462_132_088_610, auto_renewing: true, developer_payload: "payload that gets stored and returned", payment_state: 1, @@ -137,11 +138,11 @@ describe "when subscription is canceled" do let(:fake_subscription_purchase) do - FakeSubscriptionPurchase.new( + OpenStruct.new( kind: "androidpublisher#subscriptionPurchase", - start_time_millis: 1459540113244, - expiry_time_millis: 1462132088610, - user_cancellation_time_millis: 1461872888000, + start_time_millis: 1_459_540_113_244, + expiry_time_millis: 1_462_132_088_610, + user_cancellation_time_millis: 1_461_872_888_000, auto_renewing: true, developer_payload: "payload that gets stored and returned", cancel_reason: 0, @@ -166,10 +167,10 @@ describe "expired with pending payment" do let(:fake_subscription_purchase) do - FakeSubscriptionPurchase.new( + OpenStruct.new( kind: "androidpublisher#subscriptionPurchase", - start_time_millis: 1459540113244, - expiry_time_millis: 1462132088610, + start_time_millis: 1_459_540_113_244, + expiry_time_millis: 1_462_132_088_610, auto_renewing: true, developer_payload: "payload that gets stored and returned", cancel_reason: 0, @@ -188,10 +189,10 @@ describe "trial" do let(:fake_subscription_purchase) do - FakeSubscriptionPurchase.new( + OpenStruct.new( kind: "androidpublisher#subscriptionPurchase", - start_time_millis: 1459540113244, - expiry_time_millis: 1462132088610, + start_time_millis: 1_459_540_113_244, + expiry_time_millis: 1_462_132_088_610, auto_renewing: false, developer_payload: "payload that gets stored and returned", cancel_reason: 0, @@ -209,29 +210,4 @@ _(subject.price_currency_code).must_equal "SOMECODE" end end - - private - - class FakeSubscriptionPurchase - FIELDS = [ - :kind, - :start_time_millis, - :expiry_time_millis, - :user_cancellation_time_millis, - :auto_renewing, - :developer_payload, - :cancel_reason, - :payment_state, - :price_amount_micros, - :price_currency_code, - ].freeze - - attr_accessor(*FIELDS) - - def initialize(hash) - FIELDS.each do |key| - self.public_send("#{key}=", hash[key]) - end - end - end end diff --git a/spec/play_store/verification_failure_spec.rb b/spec/play_store/verification_failure_spec.rb index fa31f5f..ec579a3 100644 --- a/spec/play_store/verification_failure_spec.rb +++ b/spec/play_store/verification_failure_spec.rb @@ -3,9 +3,13 @@ describe CandyCheck::PlayStore::VerificationFailure do subject { CandyCheck::PlayStore::VerificationFailure.new(fake_error) } + let(:fake_error_class) do + Struct.new(:status_code, :message) + end + describe "denied" do let(:fake_error) do - FakeError.new("401", "The current user has insufficient permissions") + fake_error_class.new("401", "The current user has insufficient permissions") end it "returns the code" do @@ -19,7 +23,7 @@ describe "empty" do let(:fake_error) do - FakeError.new(nil, nil) + fake_error_class.new(nil, nil) end it "returns an unknown code" do @@ -30,6 +34,4 @@ _(subject.message).must_equal "Unknown error" end end - - FakeError = Struct.new(:status_code, :message) end diff --git a/spec/play_store/verifier_spec.rb b/spec/play_store/verifier_spec.rb index 74e9cc8..9c5863e 100644 --- a/spec/play_store/verifier_spec.rb +++ b/spec/play_store/verifier_spec.rb @@ -32,14 +32,16 @@ describe "subscription purchases" do it "verifies a subscription purchase" do VCR.use_cassette("play_store/subscription_purchases/valid_but_expired") do - result = subject.verify_subscription_purchase(package_name: package_name, subscription_id: subscription_id, token: token) + result = subject.verify_subscription_purchase(package_name: package_name, subscription_id: subscription_id, + token: token) _(result).must_be_instance_of CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionPurchase end end it "can return a subscription purchase verification failure" do VCR.use_cassette("play_store/subscription_purchases/permission_denied") do - result = subject.verify_subscription_purchase(package_name: package_name, subscription_id: subscription_id, token: token) + result = subject.verify_subscription_purchase(package_name: package_name, subscription_id: subscription_id, + token: token) _(result).must_be_instance_of CandyCheck::PlayStore::VerificationFailure end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 70b6004..8b10098 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,3 @@ - require "candy_check" require "candy_check/cli" @@ -38,14 +37,13 @@ def in_continuous_integration_environment? ENV["DEBUG"] && Google::APIClient.logger.level = Logger::DEBUG -class MiniTest::Spec - class << self - alias :context :describe +module MiniTest + class Spec + class << self + alias context describe + end end -end - -module MiniTest module Assertions # The first parameter must be ```true```, not coercible to true. def assert_true(obj, msg = nil)