diff --git a/Gemfile b/Gemfile index a0a40047..ab0ef624 100644 --- a/Gemfile +++ b/Gemfile @@ -90,4 +90,5 @@ gem "solargraph-rails", "~> 1.1", group: :development # For automated tests. group :development, :test do gem "rspec-rails", "~> 7.0" + gem "webmock", "~> 3.24", group: :test end diff --git a/Gemfile.lock b/Gemfile.lock index c80f2416..4adbd8f1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -128,6 +128,9 @@ GEM fiber-annotation fiber-local (~> 1.1) json + crack (1.0.0) + bigdecimal + rexml crass (1.0.6) csv (3.3.0) date (3.3.4) @@ -182,6 +185,7 @@ GEM temple (>= 0.8.2) thor tilt + hashdiff (1.1.2) hashie (5.0.0) http_accept_language (2.1.1) httparty (0.22.0) @@ -496,6 +500,10 @@ GEM activesupport faraday (~> 2.0) faraday-follow_redirects + webmock (3.24.0) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) webrick (1.8.2) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) @@ -549,6 +557,7 @@ DEPENDENCIES thread-local (~> 1.1) turbo-rails (~> 2.0) web-console (~> 4.2) + webmock (~> 3.24) will_paginate (~> 4.0) RUBY VERSION diff --git a/app/services/neopets/nc_mall.rb b/app/services/neopets/nc_mall.rb index 86ae2b09..5ae94170 100644 --- a/app/services/neopets/nc_mall.rb +++ b/app/services/neopets/nc_mall.rb @@ -45,6 +45,34 @@ module Neopets::NCMall uniq end + STYLING_STUDIO_URL = "https://www.neopets.com/np-templates/ajax/stylingstudio/studio.php" + def self.load_styles(species_id:, neologin:) + Sync do + INTERNET.post( + STYLING_STUDIO_URL, + headers: [ + ["User-Agent", Rails.configuration.user_agent_for_neopets], + ["Content-Type", "application/x-www-form-urlencoded"], + ["Cookie", "neologin=#{neologin}"], + ["X-Requested-With", "XMLHttpRequest"], + ], + body: {tab: 1, mode: "getStyles", species: species_id}.to_query, + ) do |response| + if response.status != 200 + raise ResponseNotOK.new(response.status), + "expected status 200 but got #{response.status} (#{STYLING_STUDIO_URL})" + end + + begin + data = JSON.parse(response.read).deep_symbolize_keys + data.fetch(:styles).values + rescue JSON::ParserError, KeyError + raise UnexpectedResponseFormat + end + end + end + end + private def self.load_page_by_url(url) diff --git a/spec/services/nc_mall_spec.rb b/spec/services/nc_mall_spec.rb new file mode 100644 index 00000000..656f4fa5 --- /dev/null +++ b/spec/services/nc_mall_spec.rb @@ -0,0 +1,80 @@ +require 'rails_helper' +require 'webmock/rspec' + +RSpec.describe Neopets::NCMall, type: :model do + describe ".load_styles" do + def stub_styles_request + stub_request(:post, "https://www.neopets.com/np-templates/ajax/stylingstudio/studio.php"). + with( + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", + "Cookie": "neologin=STUB_NEOLOGIN" + }, + body: "mode=getStyles&species=2&tab=1", + ) + end + + subject(:styles) do + Neopets::NCMall.load_styles( + species_id: 2, + neologin: "STUB_NEOLOGIN", + ) + end + + it "loads current NC styles from the NC Mall" do + stub_styles_request.to_return( + body: '{"success":true,"styles":{"87966":{"oii":87966,"name":"Nostalgic Alien Aisha","image":"https:\/\/images.neopets.com\/items\/nostalgic_alien_aisha.gif","limited":false},"87481":{"oii":87481,"name":"Nostalgic Sponge Aisha","image":"https:\/\/images.neopets.com\/items\/nostalgic_sponge_aisha.gif","limited":false},"90031":{"oii":90031,"name":"Celebratory Anniversary Aisha","image":"https:\/\/images.neopets.com\/items\/624dc08bcf.gif","limited":true},"90050":{"oii":90050,"name":"Nostalgic Tyrannian Aisha","image":"https:\/\/images.neopets.com\/items\/b225e06541.gif","limited":true}}}', + ) + + expect(styles).to contain_exactly( + { + oii: 87481, + name: "Nostalgic Sponge Aisha", + image: "https://images.neopets.com/items/nostalgic_sponge_aisha.gif", + limited: false, + }, + { + oii: 87966, + name: "Nostalgic Alien Aisha", + image: "https://images.neopets.com/items/nostalgic_alien_aisha.gif", + limited: false, + }, + { + oii: 90031, + name: "Celebratory Anniversary Aisha", + image: "https://images.neopets.com/items/624dc08bcf.gif", + limited: true, + }, + { + oii: 90050, + name: "Nostalgic Tyrannian Aisha", + image: "https://images.neopets.com/items/b225e06541.gif", + limited: true, + } + ) + end + + it "raises an error if the request returns a non-200 status" do + stub_styles_request.to_return(status: 400) + + expect { styles }.to raise_error(Neopets::NCMall::ResponseNotOK) + end + + it "raises an error if the request returns a non-JSON response" do + stub_styles_request.to_return( + body: "Oops, this request failed for some weird reason!", + ) + + expect { styles }.to raise_error(Neopets::NCMall::UnexpectedResponseFormat) + end + + it "raises an error if the request returns unexpected JSON" do + stub_styles_request.to_return( + body: '{"success": false}', + ) + + expect { styles }.to raise_error(Neopets::NCMall::UnexpectedResponseFormat) + end + end +end diff --git a/vendor/cache/crack-1.0.0.gem b/vendor/cache/crack-1.0.0.gem new file mode 100644 index 00000000..e3cbaf45 Binary files /dev/null and b/vendor/cache/crack-1.0.0.gem differ diff --git a/vendor/cache/hashdiff-1.1.2.gem b/vendor/cache/hashdiff-1.1.2.gem new file mode 100644 index 00000000..50898a4d Binary files /dev/null and b/vendor/cache/hashdiff-1.1.2.gem differ diff --git a/vendor/cache/webmock-3.24.0.gem b/vendor/cache/webmock-3.24.0.gem new file mode 100644 index 00000000..495c9396 Binary files /dev/null and b/vendor/cache/webmock-3.24.0.gem differ