Compare commits

...

20 commits

Author SHA1 Message Date
06721f77e9 Add alt style modeling tests
I didn't do the assets part of it yet, just the style object!
2024-10-29 22:20:43 -07:00
f9be3dceb1 Oh right, use Blue Jetsam "original biology" for modded Majal_Kita 2024-10-26 14:32:47 -07:00
c9c080e74d Merge branch 'main' into modeling-tests 2024-10-25 17:33:39 -07:00
e65634d8bc Set up tests for Majal_Kita and its alt style
Not actually touching alt style yet, just the very basic stuff about
how alt style can cause loading to fail in certain extremely rare cases
(specifically, if it's our first time seeing the underlying
color/species combo too, which… isn't gonna happen irl on DTI for a long
time if ever, I would guess, but hey!)
2024-10-25 14:47:51 -07:00
4c5d14c591 Add tests for re-modeling Thyassa, in a different mood 2024-10-25 14:15:25 -07:00
28bd6ecca4 Test that modeling items twice doesn't affect them or their assets 2024-10-25 13:47:23 -07:00
7a837edaf6 Use alias they in specs instead of it, when it reads better 2024-10-25 13:38:02 -07:00
f3894759d6 Add tests for modeling item assets 2024-10-25 13:34:02 -07:00
30ada0b7e1 For biology assets, make clearer the weirdness with saving first 2024-10-25 13:04:46 -07:00
8a38ce90dc Refactor tests for saving things when pet is saved
This got more complicated with the items case, and I think this is a
stronger idiom for communicating it!
2024-10-25 12:55:31 -07:00
6d25b3424f Add tests for matts_bat's items
Not their assets yet, just the items
2024-10-24 15:58:16 -07:00
8902527438 Basic modeling spec for my pet, matts_bat
No items yet, just putting in the very basics of biology, more
shortform than the Thyassa case
2024-10-24 15:37:57 -07:00
044dface14 Add RSpec to the commit hook 2024-10-24 15:22:39 -07:00
b1890d4f6f Refactor existing modeling tests to follow more RSpec-y syntax
I'm learning how to group things and stuff!
2024-10-24 15:16:55 -07:00
3a5f33fd56 Add tests that modeling a pet a second time doesn't affect its records
That is, if everything is the same as before, we don't need to change
anything in our database!

I also learned a bit more about RSpec syntax sugars, it's cute!
2024-10-24 15:04:25 -07:00
a54a844e03 Refactor modeling spec to use a new "a_record_matching" matcher 2024-10-24 14:42:05 -07:00
3b5b13c172 Fewer slowest examples in RSpec, cuz these are fast and it's fine
I want to not turn it off entirely, so that if there's a nasty one it
becomes visible, but we don't need all that vertical space for this
small test suite rn!
2024-10-21 16:47:51 -07:00
5b1d1f0695 Add assets to modeling tests, and also uhh some other fixes
I forget, there was some tricky debugging about getting the fixtures
right, I think the previous commit doesn't *actually* pass from a clean
setting. Ah well, looks good now!
2024-10-21 16:46:10 -07:00
e92e315743 Move modeling tests to RSpec
Also note the jsbundling bump, that was so I can us the relatively-new
`SKIP_YARN_INSTALL=1` flag when running tests, to be a bit faster.
2024-10-21 16:03:58 -07:00
eb2fb125b9 Basic modeling test!
Just getting a basic foothold here. I'm thinking about moving this to
RSpec, cuz I feel like the assertions are gonna get pretty specific
and groupable.
2024-10-21 15:29:22 -07:00
23 changed files with 1489 additions and 3 deletions

1
.gitignore vendored
View file

@ -4,6 +4,7 @@ log/*.log
tmp/**/*
.env
.env.*
/spec/examples.txt
/app/assets/builds/*
!/app/assets/builds/.keep

View file

@ -1,4 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint --max-warnings=0 --fix && bin/rails test
# Run the linter, and all our tests.
yarn lint --max-warnings=0 --fix && bin/rake test spec

1
.rspec Normal file
View file

@ -0,0 +1 @@
--require spec_helper

View file

@ -19,7 +19,7 @@ gem 'haml', '~> 6.1', '>= 6.1.1'
gem 'sass-rails', '~> 6.0'
gem 'terser', '~> 1.1', '>= 1.1.17'
gem 'react-rails', '~> 2.7', '>= 2.7.1'
gem 'jsbundling-rails', '~> 1.1'
gem 'jsbundling-rails', '~> 1.3'
gem 'turbo-rails', '~> 2.0'
# For authentication.
@ -86,3 +86,8 @@ gem "shell", "~> 0.8.1"
# For workspace autocomplete.
gem "solargraph", "~> 0.50.0", group: :development
gem "solargraph-rails", "~> 1.1", group: :development
# For automated tests.
group :development, :test do
gem "rspec-rails", "~> 7.0"
end

View file

@ -376,6 +376,23 @@ GEM
reverse_markdown (2.1.1)
nokogiri
rexml (3.3.7)
rspec-core (3.13.2)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (7.0.1)
actionpack (>= 7.0)
activesupport (>= 7.0)
railties (>= 7.0)
rspec-core (~> 3.13)
rspec-expectations (~> 3.13)
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-support (3.13.1)
rubocop (1.66.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
@ -503,7 +520,7 @@ DEPENDENCIES
haml (~> 6.1, >= 6.1.1)
http_accept_language (~> 2.1, >= 2.1.1)
httparty (~> 0.22.0)
jsbundling-rails (~> 1.1)
jsbundling-rails (~> 1.3)
letter_opener (~> 1.8, >= 1.8.1)
memory_profiler (~> 1.0)
mysql2 (~> 0.5.5)
@ -518,6 +535,7 @@ DEPENDENCIES
rails-i18n (~> 7.0, >= 7.0.7)
rdiscount (~> 2.2, >= 2.2.7.1)
react-rails (~> 2.7, >= 2.7.1)
rspec-rails (~> 7.0)
sanitize (~> 6.0, >= 6.0.2)
sass-rails (~> 6.0)
sentry-rails (~> 5.12)

9
spec/fixtures/colors.yml vendored Normal file
View file

@ -0,0 +1,9 @@
purple:
id: 57
name: purple
robot:
id: 62
name: robot
striped:
id: 77
name: striped

9
spec/fixtures/species.yml vendored Normal file
View file

@ -0,0 +1,9 @@
blumaroo:
id: 3
name: blumaroo
chia:
id: 7
name: chia
jetsam:
id: 20
name: jetsam

282
spec/fixtures/zones.yml vendored Normal file
View file

@ -0,0 +1,282 @@
music:
id: 1
depth: 1
type_id: 4
label: Music
plain_label: music
soundeffects:
id: 2
depth: 2
type_id: 4
label: Sound Effects
plain_label: soundeffects
background:
id: 3
depth: 3
type_id: 3
label: Background
plain_label: background
biologyeffects:
id: 4
depth: 6
type_id: 1
label: Biology Effects
plain_label: biologyeffects
hindbiology:
id: 5
depth: 7
type_id: 1
label: Hind Biology
plain_label: hindbiology
markings:
id: 31
depth: 35
type_id: 2
label: Markings
plain_label: markings
hinddisease:
id: 7
depth: 9
type_id: 1
label: Hind Disease
plain_label: hinddisease
hindcover:
id: 8
depth: 10
type_id: 2
label: Hind Cover
plain_label: hindcover
hindtransientbiology:
id: 9
depth: 11
type_id: 1
label: Hind Transient Biology
plain_label: hindtransientbiology
hinddrippings:
id: 10
depth: 12
type_id: 1
label: Hind Drippings
plain_label: hinddrippings
backpack:
id: 11
depth: 13
type_id: 2
label: Backpack
plain_label: backpack
wingstransientbiology:
id: 12
depth: 14
type_id: 1
label: Wings Transient Biology
plain_label: wingstransientbiology
wings:
id: 13
depth: 15
type_id: 2
label: Wings
plain_label: wings
hairback:
id: 14
depth: 17
type_id: 1
label: Hair Back
plain_label: hairback
body:
id: 15
depth: 18
type_id: 1
label: Body
plain_label: body
bodydisease:
id: 17
depth: 20
type_id: 1
label: Body Disease
plain_label: bodydisease
feettransientbiology:
id: 18
depth: 21
type_id: 1
label: Feet Transient Biology
plain_label: feettransientbiology
shoes:
id: 19
depth: 22
type_id: 2
label: Shoes
plain_label: shoes
lowerbodytransientbiology:
id: 20
depth: 23
type_id: 1
label: Lower-body Transient Biology
plain_label: lowerbodytransientbiology
trousers:
id: 21
depth: 24
type_id: 2
label: Trousers
plain_label: trousers
upperbodytransientbiology:
id: 22
depth: 25
type_id: 1
label: Upper-body Transient Biology
plain_label: upperbodytransientbiology
shirtdress:
id: 23
depth: 26
type_id: 2
label: Shirt/Dress
plain_label: shirtdress
necklace:
id: 24
depth: 28
type_id: 2
label: Necklace
plain_label: necklace
gloves:
id: 25
depth: 29
type_id: 2
label: Gloves
plain_label: gloves
jacket:
id: 26
depth: 30
type_id: 2
label: Jacket
plain_label: jacket
collar:
id: 27
depth: 31
type_id: 2
label: Collar
plain_label: collar
bodydrippings:
id: 28
depth: 32
type_id: 1
label: Body Drippings
plain_label: bodydrippings
ruff:
id: 29
depth: 33
type_id: 1
label: Ruff
plain_label: ruff
head:
id: 30
depth: 34
type_id: 1
label: Head
plain_label: head
headdisease:
id: 32
depth: 36
type_id: 1
label: Head Disease
plain_label: headdisease
eyes:
id: 33
depth: 37
type_id: 1
label: Eyes
plain_label: eyes
mouth:
id: 34
depth: 38
type_id: 1
label: Mouth
plain_label: mouth
glasses:
id: 35
depth: 41
type_id: 2
label: Glasses
plain_label: glasses
earrings:
id: 41
depth: 45
type_id: 2
label: Earrings
plain_label: earrings
hairfront:
id: 37
depth: 40
type_id: 1
label: Hair Front
plain_label: hairfront
headtransientbiology:
id: 38
depth: 42
type_id: 1
label: Head Transient Biology
plain_label: headtransientbiology
headdrippings:
id: 39
depth: 43
type_id: 1
label: Head Drippings
plain_label: headdrippings
hat:
id: 50
depth: 16
type_id: 2
label: Hat
plain_label: hat
righthanditem:
id: 49
depth: 5
type_id: 2
label: Right-hand Item
plain_label: righthanditem
lefthanditem:
id: 43
depth: 47
type_id: 2
label: Left-hand Item
plain_label: lefthanditem
higherforegrounditem:
id: 44
depth: 49
type_id: 3
label: Higher Foreground Item
plain_label: higherforegrounditem
lowerforegrounditem:
id: 45
depth: 50
type_id: 3
label: Lower Foreground Item
plain_label: lowerforegrounditem
static:
id: 46
depth: 48
type_id: 2
label: Static
plain_label: static
thoughtbubble:
id: 47
depth: 51
type_id: 3
label: Thought Bubble
plain_label: thoughtbubble
backgrounditem:
id: 48
depth: 4
type_id: 3
label: Background Item
plain_label: backgrounditem
belt:
id: 51
depth: 27
type_id: 2
label: Belt
plain_label: belt
foreground:
id: 52
depth: 52
type_id: 3
label: Foreground
plain_label: foreground

440
spec/models/pet_spec.rb Normal file
View file

@ -0,0 +1,440 @@
require 'rails_helper'
require_relative '../support/mocks/custom_pets'
require_relative '../support/matchers/a_record_matching'
RSpec.describe Pet, type: :model do
fixtures :colors, :species, :zones
context ".load" do
context "for thyassa, the Purple Chia" do
subject(:pet) { Pet.load "thyassa" }
it("is named thyassa") { expect(pet.name).to eq("thyassa") }
it("has no items") { expect(pet.items).to be_empty }
describe "its pet type" do
subject(:pet_type) { pet.pet_type }
it("is new and unsaved") { should be_new_record }
it("is Purple") { expect(pet_type.color).to eq Color.find_by_name!("purple") }
it("is a Chia") { expect(pet_type.species).to eq Species.find_by_name!("chia") }
it("has the standard Chia body") { expect(pet_type.body_id).to eq 212 }
it("uses the pet's image hash") { expect(pet_type.image_hash).to eq "m:thyass" }
it("is saved when saving the pet") { pet.save!; should be_persisted }
end
describe "its pet state" do
subject(:pet_state) { pet.pet_state }
it("is new and unsaved") { should be_new_record }
it("belongs to the pet's pet type") { expect(pet_state.pet_type).to eq pet.pet_type }
it("isn't labeled yet") { expect(pet_state.pose).to eq "UNKNOWN" }
it("is saved when saving the pet") { pet.save!; should be_persisted }
describe "its biology assets" do
# TODO: I wish biology assets were set up before saving.
# Once we change this, we can un-mark some tests as pending.
before { pet.save! }
subject(:biology_assets) { pet_state.swf_assets }
let(:asset_ids) { biology_assets.map(&:remote_id) }
they("are all new") do
pending("Currently, pets must be saved before assets are assigned.")
should all be_new_record
end
they("match the expected IDs") do
expect(asset_ids).to contain_exactly(10083, 11613, 14187, 14189)
end
they("are saved when saving the pet") { pet.save!; should all be_persisted }
they("have the expected asset metadata") do
expect(pet_state.swf_assets).to contain_exactly(
a_record_matching(
type: "biology",
remote_id: 10083,
zone_id: 37,
url: "https://images.neopets.com/cp/bio/swf/000/000/010/10083_8a1111a13f.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/010/10083_8a1111a13f/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
type: "biology",
remote_id: 11613,
zone_id: 15,
url: "https://images.neopets.com/cp/bio/swf/000/000/011/11613_f7d8d377ab.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/011/11613_f7d8d377ab/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
type: "biology",
remote_id: 14187,
zone_id: 34,
url: "https://images.neopets.com/cp/bio/swf/000/000/014/14187_0e65c2082f.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/014/14187_0e65c2082f/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
type: "biology",
remote_id: 14189,
zone_id: 33,
url: "https://images.neopets.com/cp/bio/swf/000/000/014/14189_102e4991e9.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/014/14189_102e4991e9/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
)
)
end
end
end
context "when modeled a second time" do
before { pet.save! }
subject!(:new_pet) { Pet.load("thyassa") }
describe "its pet type" do
subject(:pet_type) { new_pet.pet_type }
it("already exists") { should be_persisted }
it("is the same as before") { should eq pet.pet_type }
it "is not changed when saving the pet" do
expect { new_pet.save! }.not_to change { pet_type.attributes }
end
end
describe "its pet state" do
subject(:pet_state) { new_pet.pet_state }
it("already exists") { should be_persisted }
it("is the same as before") { should eq pet.pet_state }
it "is not changed when saving the pet" do
expect { new_pet.save! }.not_to change { pet_state.attributes }
end
end
describe "its biology assets" do
subject(:biology_assets) { new_pet.pet_state.swf_assets }
they("already exist") { should all be_persisted }
they("are the same as before") { should eq pet.pet_state.swf_assets }
they("are not changed when saving the pet") do
expect { new_pet.save! }.not_to change { biology_assets.map(&:attributes) }
end
end
end
context "when modeled again, but happy" do
before { pet.save! }
subject(:new_pet) { Pet.load("thyassa:happy") }
describe "its pet type" do
subject(:pet_type) { new_pet.pet_type }
it("already exists") { should be_persisted }
it("is the same as before") { should eq pet.pet_type }
it "is not changed when saving the pet" do
expect { new_pet.save! }.not_to change { pet_type.attributes }
end
end
describe "its pet state" do
subject(:pet_state) { new_pet.pet_state }
it("is new and unsaved") { should be_new_record }
it("belongs to the same pet type") { expect(pet_state.pet_type).to eq pet.pet_type }
it("isn't labeled yet") { expect(pet_state.pose).to eq "UNKNOWN" }
it("is saved when saving the pet") { new_pet.save!; should be_persisted }
describe "its biology assets" do
# TODO: I wish biology assets were set up before saving.
# Once we change this, we can un-mark some tests as pending.
before { new_pet.save! }
subject(:biology_assets) { pet_state.swf_assets }
let(:asset_ids) { biology_assets.map(&:remote_id) }
they("are partially new, partially existing") do
pending("Currently, pets must be saved before assets are assigned.")
fail # TODO: Write this test once we have the ability to see it pass!
end
they("match the expected IDs") do
expect(asset_ids).to contain_exactly(10083, 11613, 10448, 10451)
end
they("are saved when saving the pet") { new_pet.save!; should all be_persisted }
they("have the expected asset metadata") do
expect(pet_state.swf_assets).to contain_exactly(
a_record_matching(
type: "biology",
remote_id: 10083,
zone_id: 37,
url: "https://images.neopets.com/cp/bio/swf/000/000/010/10083_8a1111a13f.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/010/10083_8a1111a13f/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
type: "biology",
remote_id: 11613,
zone_id: 15,
url: "https://images.neopets.com/cp/bio/swf/000/000/011/11613_f7d8d377ab.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/011/11613_f7d8d377ab/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
type: "biology",
remote_id: 10448,
zone_id: 34,
url: "https://images.neopets.com/cp/bio/swf/000/000/010/10448_0b238e79e2.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/010/10448_0b238e79e2/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
type: "biology",
remote_id: 10451,
zone_id: 33,
url: "https://images.neopets.com/cp/bio/swf/000/000/010/10451_cd4a8a8e47.swf",
manifest_url: "https://images.neopets.com/cp/bio/data/000/000/010/10451_cd4a8a8e47/manifest.json",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
)
)
end
end
end
end
end
context "for matts_bat, a pet with basic items" do
subject(:pet) { Pet.load("matts_bat") }
# We do simpler checks for biology, and trust the Thyassa case to cover it.
it("is named matts_bat") { expect(pet.name).to eq "matts_bat" }
it("is a Striped Blumaroo") { expect(pet.pet_type.human_name).to eq "Striped Blumaroo" }
describe "its biology assets" do
# TODO: I wish biology assets were set up before saving.
# Once we change this, we can un-mark some tests as pending.
before { pet.save! }
subject(:biology_assets) { pet.pet_state.swf_assets }
let(:asset_ids) { biology_assets.map(&:remote_id) }
they("are all new") do
pending("Currently, pets must be saved before assets are assigned.")
should all be_new_record
end
they("match the expected IDs") do
expect(asset_ids).to contain_exactly(331, 332, 333, 23760, 23411)
end
they("are saved when saving the pet") { pet.save!; should all be_persisted }
end
describe "its items" do
subject(:items) { pet.items }
let(:item_ids) { items.map(&:id) }
they("are all new") { should all be_new_record }
they("match the expected IDs") do
expect(item_ids).to contain_exactly(39552, 53874, 71706)
end
they("are saved when saving the pet") { pet.save! ; should all be_persisted }
they("have the expected item metadata") do
should contain_exactly(
a_record_matching(
id: 39552,
name: "Springy Eye Glasses",
description: "Hey, keep your eyes in your head!",
thumbnail_url: "https://images.neopets.com/items/mall_springyeyeglasses.gif",
category: "Clothes",
type: "Clothes",
rarity: "Artifact",
rarity_index: 500,
price: 0,
weight_lbs: 1,
species_support_ids: "3",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
id: 53874,
name: "404 Shirt",
description: "When Neopets is down, the shirt comes on!",
thumbnail_url: "https://images.neopets.com/items/clo_404_shirt.gif",
category: "Clothes",
type: "Clothes",
rarity: "Rare",
rarity_index: 88,
price: 1701,
weight_lbs: 1,
species_support_ids: "3",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
a_record_matching(
id: 71706,
name: "On the Roof Background",
description: "Who is that on the roof?! Could it be...?",
thumbnail_url: "https://images.neopets.com/items/gif_roof_onthe_fg.gif",
category: "Special",
type: "Mystical Surroundings",
rarity: "Special",
rarity_index: 101,
price: 0,
weight_lbs: 1,
species_support_ids: "",
zones_restrict: "0000000000000000000000000000000000000000000000000000",
),
)
end
end
context "its item assets" do
# TODO: I wish item assets were set up before saving.
# Once we change this, we can un-mark some tests as pending.
before { pet.save! }
let(:assets_by_item) { pet.items.to_h { |item| [item.id, item.swf_assets.to_a] } }
subject(:item_assets) { assets_by_item.values.flatten(1) }
let(:asset_ids) { item_assets.map(&:remote_id) }
they("are all new") do
pending("Currently, pets must be saved before assets are assigned.")
should all be_new_record
end
they("match the expected IDs") do
expect(asset_ids).to contain_exactly(16933, 108567, 410722)
end
they("are saved when saving the pet") { pet.save! ; should all be_persisted }
they("match the expected metadata") do
expect(assets_by_item).to match(
39552 => a_collection_containing_exactly(
a_record_matching(
type: "object",
remote_id: 16933,
zone_id: 35,
url: "https://images.neopets.com/cp/items/swf/000/000/016/16933_0833353c4f.swf",
manifest_url: "https://images.neopets.com/cp/items/data/000/000/016/16933_0833353c4f/manifest.json?v=1706",
zones_restrict: "",
)
),
53874 => a_collection_containing_exactly(
a_record_matching(
type: "object",
remote_id: 108567,
zone_id: 23,
url: "https://images.neopets.com/cp/items/swf/000/000/108/108567_ee88141325.swf",
manifest_url: "https://images.neopets.com/cp/items/data/000/000/108/108567_ee88141325/manifest.json?v=1706",
zones_restrict: "",
)
),
71706 => a_collection_containing_exactly(
a_record_matching(
type: "object",
remote_id: 410722,
zone_id: 3,
url: "https://images.neopets.com/cp/items/swf/000/000/410/410722_3bcd2f5e11.swf",
manifest_url: "https://images.neopets.com/cp/items/data/000/000/410/410722_3bcd2f5e11/manifest.json?v=1706",
zones_restrict: "",
)
),
)
end
end
context "when modeled a second time" do
before { pet.save! }
subject!(:new_pet) { Pet.load("matts_bat") }
describe "its pet type" do
subject(:pet_type) { new_pet.pet_type }
it("already exists") { should be_persisted }
it("is the same as before") { should eq pet.pet_type }
it "is not changed when saving the pet" do
expect { new_pet.save! }.not_to change { pet_type.attributes }
end
end
describe "its pet state" do
subject(:pet_state) { new_pet.pet_state }
it("already exists") { should be_persisted }
it("is the same as before") { should eq pet.pet_state }
it "is not changed when saving the pet" do
expect { new_pet.save! }.not_to change { pet_state.attributes }
end
end
describe "its biology assets" do
subject(:biology_assets) { new_pet.pet_state.swf_assets }
they("already exist") { should all be_persisted }
they("are the same as before") { should eq pet.pet_state.swf_assets }
they("are not changed when saving the pet") do
expect { new_pet.save! }.not_to change { biology_assets.map(&:attributes) }
end
end
describe "its items" do
subject(:items) { new_pet.items }
they("already exist") { should all be_persisted }
they("are the same as before") { should eq pet.items }
they("are not changed when saving the pet") do
expect { new_pet.save! }.not_to change { items.map(&:attributes) }
end
end
describe "its item assets" do
subject(:item_assets) { new_pet.items.map(&:swf_assets).flatten(1) }
they("already exist") { should all be_persisted }
they("are the same as before") { should eq pet.items.map(&:swf_assets).flatten(1) }
they("are not changed when saving the pet") do
expect { new_pet.save! }.not_to change { item_assets.map(&:attributes) }
end
end
end
end
context "for Majal_Kita, the Nostalgic Robot Jetsam (modded to be Blue as its base)" do
it("fails to load without an existing Blue Jetsam, " +
"because the biology data is incomplete") do
expect { Pet.load("Majal_Kita") }.to raise_error(Pet::UnexpectedDataFormat)
end
context "with a Blue Jetsam already modeled" do
before { Pet.load("Blue_Jetsam").save! }
subject(:pet) { Pet.load("Majal_Kita") }
it("loads without raising an error") do
expect { Pet.load("Majal_Kita") }.not_to raise_error
end
describe "its alt style" do
subject(:alt_style) { pet.alt_style }
it("is new and unsaved") { should be_new_record }
it("has the unique ID 87458") { expect(alt_style.id).to eq 87458 }
it("is Robot") { expect(alt_style.color).to eq Color.find_by_name!("Robot") }
it("is a Jetsam") { expect(alt_style.species).to eq Species.find_by_name!("Jetsam") }
it("has unique body ID 378") { expect(alt_style.body_id).to eq 378 }
it("has no series name yet") { expect(alt_style.real_series_name?).to be false }
it("has no thumbnail yet") { expect(alt_style.thumbnail_url?).to be false }
it("is saved when saving the pet") { pet.save!; should be_persisted }
end
# TODO: Alt style assets!
context "when modeled a second time" do
before { pet.save! }
subject(:new_pet) { Pet.load("Majal_Kita") }
describe "its alt style" do
subject(:alt_style) { new_pet.alt_style }
it("already exists") { should be_persisted }
it("is the same as before") { should eq pet.alt_style }
it "is not changed when saving the pet" do
expect { new_pet.save! }.not_to change { alt_style.attributes }
end
end
end
end
end
end
end

68
spec/rails_helper.rb Normal file
View file

@ -0,0 +1,68 @@
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
# Uncomment the line below in case you have `--require rails_helper` in the `.rspec` file
# that will avoid rails generators crashing because migrations haven't been run yet
# return unless Rails.env.test?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).each { |f| require f }
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
abort e.to_s.strip
end
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_paths = [
Rails.root.join('spec/fixtures')
]
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# You can uncomment this line to turn off ActiveRecord support entirely.
# config.use_active_record = false
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, type: :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://rspec.info/features/7-0/rspec-rails
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end

87
spec/spec_helper.rb Normal file
View file

@ -0,0 +1,87 @@
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
# Some of our SWF assets are long, and we want all of it when diffing!
# Here, I picked a large number, to approximate "any length" but not
# actually allow infinity.
expectations.max_formatted_output_length = 3000
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
# In our specs, we describe plural collections sometimes, so we add "they"
# as an alias for "it".
config.alias_example_to :they
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
# have no way to turn it off -- the option exists only for backwards
# compatibility in RSpec 3). It causes shared context metadata to be
# inherited by the metadata hash of host groups and examples, rather than
# triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
# This allows you to limit a spec run to individual examples or groups
# you care about by tagging them with `:focus` metadata. When nothing
# is tagged with `:focus`, all examples get run. RSpec also provides
# aliases for `it`, `describe`, and `context` that include `:focus`
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
config.filter_run_when_matching :focus
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options. We recommend
# you configure your source control system to ignore this file.
config.example_status_persistence_file_path = "spec/examples.txt"
# Limits the available syntax to the non-monkey patched syntax that is
# recommended. For more details, see:
# https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/
config.disable_monkey_patching!
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = "doc"
end
# Print some of the slowest examples and example groups at the
# end of the spec run, to help surface which specs are running
# particularly slow.
config.profile_examples = 2
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
end

View file

@ -0,0 +1,7 @@
RSpec::Matchers.define :a_record_matching do |expected|
match do |actual|
expected.all? do |attr_name, expected_value|
actual.read_attribute(attr_name) == expected_value
end
end
end

View file

@ -0,0 +1,18 @@
# We replace Neopets::CustomPets methods with a mocked implementation.
module Neopets::CustomPets
DATA_DIR = Pathname.new(__dir__) / "custom_pets"
def self.fetch_viewer_data(pet_name, ...)
File.open(DATA_DIR / "#{pet_name}.json") do |file|
HashWithIndifferentAccess.new JSON.load(file)
end
end
def self.fetch_metadata(...)
raise NotImplementedError
end
def self.fetch_image_hash(pet_name, ...)
"m:#{pet_name}"[0, 8] # A mock hash, like `m:thyass` for "thyassa".
end
end

View file

@ -0,0 +1,57 @@
{
"custom_pet": {
"name": "Blue_Jetsam",
"owner": "puff_p0wer",
"slot": 1.0,
"scale": 0.5,
"muted": false,
"body_id": 147.0,
"species_id": 20.0,
"color_id": 8.0,
"alt_style": false,
"alt_color": 8.0,
"style_closet_id": null,
"biology_by_zone": {
"5": {
"part_id": 4875.0,
"zone_id": 5.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/004/4875_e67d21406c.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/004/4875_e67d21406c/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"30": {
"part_id": 4873.0,
"zone_id": 30.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/004/4873_bf5b8873c3.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/004/4873_bf5b8873c3/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"15": {
"part_id": 4874.0,
"zone_id": 15.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/004/4874_86483dd3a2.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/004/4874_86483dd3a2/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"34": {
"part_id": 16312.0,
"zone_id": 34.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/016/16312_d6ecd5c181.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/016/16312_d6ecd5c181/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"33": {
"part_id": 16311.0,
"zone_id": 33.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/016/16311_aec9557cea.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/016/16311_aec9557cea/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
}
},
"equipped_by_zone": [],
"original_biology": []
},
"closet_items": [],
"object_info_registry": [],
"object_asset_registry": []
}

View file

@ -0,0 +1,187 @@
{
"dti_comment": "I modded this to be a Blue Jetsam based underneath the alt style! The actual Majal_Kita is a Robot in both base color and alt style color.",
"custom_pet": {
"name": "Majal_Kita",
"owner": "featheralley",
"slot": 1.0,
"scale": 0.5,
"muted": false,
"body_id": 378.0,
"species_id": 20.0,
"color_id": 8.0,
"alt_style": 87458.0,
"alt_color": 62.0,
"style_closet_id": "55239",
"biology_by_zone": {
"15": {
"part_id": 56223.0,
"zone_id": 15.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/056/56223_dc26edc764.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/056/56223_dc26edc764/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
}
},
"equipped_by_zone": {
"45": {
"asset_id": 119596.0,
"zone_id": 45.0,
"closet_obj_id": 32579616.0
},
"48": {
"asset_id": 128775.0,
"zone_id": 48.0,
"closet_obj_id": 32579617.0
},
"3": {
"asset_id": 250486.0,
"zone_id": 3.0,
"closet_obj_id": 32579618.0
}
},
"original_biology": {
"5": {
"part_id": 4875.0,
"zone_id": 5.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/004/4875_e67d21406c.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/004/4875_e67d21406c/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"30": {
"part_id": 4873.0,
"zone_id": 30.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/004/4873_bf5b8873c3.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/004/4873_bf5b8873c3/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"15": {
"part_id": 4874.0,
"zone_id": 15.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/004/4874_86483dd3a2.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/004/4874_86483dd3a2/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"34": {
"part_id": 16312.0,
"zone_id": 34.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/016/16312_d6ecd5c181.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/016/16312_d6ecd5c181/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"33": {
"part_id": 16311.0,
"zone_id": 33.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/016/16311_aec9557cea.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/016/16311_aec9557cea/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
}
}
},
"closet_items": {
"32579616": {
"closet_obj_id": 32579616.0,
"obj_info_id": 54794.0,
"applied_to": "Majal_Kita",
"is_wishlist": false,
"expiration": "N/A"
},
"32579617": {
"closet_obj_id": 32579617.0,
"obj_info_id": 55779.0,
"applied_to": "Majal_Kita",
"is_wishlist": false,
"expiration": "N/A"
},
"32579618": {
"closet_obj_id": 32579618.0,
"obj_info_id": 65557.0,
"applied_to": "Majal_Kita",
"is_wishlist": false,
"expiration": "N/A"
}
},
"object_info_registry": {
"54794": {
"obj_info_id": 54794.0,
"assets_by_zone": {
"45": 119596.0
},
"zones_restrict": "0000000000000000000000000000000000000000000000000000",
"is_compatible": true,
"is_paid": false,
"thumbnail_url": "https://images.neopets.com/items/gif_petpetcannonballcan.gif",
"name": "Petpet Cannonball Cannon",
"description": "We hope you dont use this cannon to shoot Petpets out of! This prize was awarded by AAA for beating his Daily Dare score in Y13.",
"category": "Gift",
"type": "Gift",
"rarity": "Special",
"rarity_index": 101.0,
"price": 0.0,
"weight_lbs": 1.0,
"species_support": [],
"converted": true
},
"55779": {
"obj_info_id": 55779.0,
"assets_by_zone": {
"48": 128775.0
},
"zones_restrict": "0000000000000000000000000000000000000000000000000000",
"is_compatible": true,
"is_paid": true,
"thumbnail_url": "https://images.neopets.com/items/mall_garland_sheilds_weapons.gif",
"name": "Shields and Weapons Garland",
"description": "My, thats a rather heavily defended decoration, isnt it?",
"category": "Special",
"type": "Special",
"rarity": "Artifact",
"rarity_index": 500.0,
"price": 0.0,
"weight_lbs": 1.0,
"species_support": [],
"converted": true
},
"65557": {
"obj_info_id": 65557.0,
"assets_by_zone": {
"3": 250486.0
},
"zones_restrict": "0000000000000000000000000000000000000000000000000000",
"is_compatible": true,
"is_paid": true,
"thumbnail_url": "https://images.neopets.com/items/mall_bg_prebattle.gif",
"name": "Approaching Battle Background",
"description": "Better stock up on supplies before they arrive!",
"category": "Special",
"type": "Special",
"rarity": "Artifact",
"rarity_index": 500.0,
"price": 0.0,
"weight_lbs": 1.0,
"species_support": [],
"converted": true
}
},
"object_asset_registry": {
"119596": {
"asset_id": 119596.0,
"zone_id": 45.0,
"asset_url": "https://images.neopets.com/cp/items/swf/000/000/119/119596_b51cbb59b8.swf",
"obj_info_id": 54794.0,
"manifest": "https://images.neopets.com/cp/items/data/000/000/119/119596_b51cbb59b8/manifest.json?v=1706"
},
"128775": {
"asset_id": 128775.0,
"zone_id": 48.0,
"asset_url": "https://images.neopets.com/cp/items/swf/000/000/128/128775_a89be479b6.swf",
"obj_info_id": 55779.0,
"manifest": "https://images.neopets.com/cp/items/data/000/000/128/128775_a89be479b6/manifest.json?v=1706"
},
"250486": {
"asset_id": 250486.0,
"zone_id": 3.0,
"asset_url": "https://images.neopets.com/cp/items/swf/000/000/250/250486_348279370a.swf",
"obj_info_id": 65557.0,
"manifest": "https://images.neopets.com/cp/items/data/000/000/250/250486_348279370a/manifest.json?v=1706"
}
}
}

View file

@ -0,0 +1,186 @@
{
"custom_pet": {
"name": "matts_bat",
"owner": "matchu1993",
"slot": 1.0,
"scale": 0.5,
"muted": true,
"body_id": 47.0,
"species_id": 3.0,
"color_id": 77.0,
"alt_style": false,
"alt_color": 77.0,
"style_closet_id": null,
"biology_by_zone": {
"30": {
"part_id": 331.0,
"zone_id": 30.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/000/331_08520cfb4c.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/000/331_08520cfb4c/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"15": {
"part_id": 332.0,
"zone_id": 15.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/000/332_cb12110826.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/000/332_cb12110826/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"5": {
"part_id": 333.0,
"zone_id": 5.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/000/333_990e61a81a.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/000/333_990e61a81a/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"34": {
"part_id": 23760.0,
"zone_id": 34.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/023/23760_400c08010f.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/023/23760_400c08010f/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"33": {
"part_id": 23411.0,
"zone_id": 33.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/023/23411_7313164efd.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/023/23411_7313164efd/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
}
},
"equipped_by_zone": {
"35": {
"asset_id": 16933.0,
"zone_id": 35.0,
"closet_obj_id": 2549145.0
},
"23": {
"asset_id": 108567.0,
"zone_id": 23.0,
"closet_obj_id": 16955628.0
},
"3": {
"asset_id": 410722.0,
"zone_id": 3.0,
"closet_obj_id": 17147987.0
}
},
"original_biology": [
]
},
"closet_items": {
"2549145": {
"closet_obj_id": 2549145.0,
"obj_info_id": 39552.0,
"applied_to": "matts_bat",
"is_wishlist": false,
"expiration": "N/A"
},
"16955628": {
"closet_obj_id": 16955628.0,
"obj_info_id": 53874.0,
"applied_to": "matts_bat",
"is_wishlist": false,
"expiration": "N/A"
},
"17147987": {
"closet_obj_id": 17147987.0,
"obj_info_id": 71706.0,
"applied_to": "matts_bat",
"is_wishlist": false,
"expiration": "N/A"
}
},
"object_info_registry": {
"39552": {
"obj_info_id": 39552.0,
"assets_by_zone": {
"35": 16933.0
},
"zones_restrict": "0000000000000000000000000000000000000000000000000000",
"is_compatible": true,
"is_paid": true,
"thumbnail_url": "https://images.neopets.com/items/mall_springyeyeglasses.gif",
"name": "Springy Eye Glasses",
"description": "Hey, keep your eyes in your head!",
"category": "Clothes",
"type": "Clothes",
"rarity": "Artifact",
"rarity_index": 500.0,
"price": 0.0,
"weight_lbs": 1.0,
"species_support": [
3.0
],
"converted": true
},
"53874": {
"obj_info_id": 53874.0,
"assets_by_zone": {
"23": 108567.0
},
"zones_restrict": "0000000000000000000000000000000000000000000000000000",
"is_compatible": true,
"is_paid": false,
"thumbnail_url": "https://images.neopets.com/items/clo_404_shirt.gif",
"name": "404 Shirt",
"description": "When Neopets is down, the shirt comes on!",
"category": "Clothes",
"type": "Clothes",
"rarity": "Rare",
"rarity_index": 88.0,
"price": 1701.0,
"weight_lbs": 1.0,
"species_support": [
3.0
],
"converted": true
},
"71706": {
"obj_info_id": 71706.0,
"assets_by_zone": {
"3": 410722.0
},
"zones_restrict": "0000000000000000000000000000000000000000000000000000",
"is_compatible": true,
"is_paid": false,
"thumbnail_url": "https://images.neopets.com/items/gif_roof_onthe_fg.gif",
"name": "On the Roof Background",
"description": "Who is that on the roof?! Could it be...?",
"category": "Special",
"type": "Mystical Surroundings",
"rarity": "Special",
"rarity_index": 101.0,
"price": 0.0,
"weight_lbs": 1.0,
"species_support": [
],
"converted": true
}
},
"object_asset_registry": {
"16933": {
"asset_id": 16933.0,
"zone_id": 35.0,
"asset_url": "https://images.neopets.com/cp/items/swf/000/000/016/16933_0833353c4f.swf",
"obj_info_id": 39552.0,
"manifest": "https://images.neopets.com/cp/items/data/000/000/016/16933_0833353c4f/manifest.json?v=1706"
},
"108567": {
"asset_id": 108567.0,
"zone_id": 23.0,
"asset_url": "https://images.neopets.com/cp/items/swf/000/000/108/108567_ee88141325.swf",
"obj_info_id": 53874.0,
"manifest": "https://images.neopets.com/cp/items/data/000/000/108/108567_ee88141325/manifest.json?v=1706"
},
"410722": {
"asset_id": 410722.0,
"zone_id": 3.0,
"asset_url": "https://images.neopets.com/cp/items/swf/000/000/410/410722_3bcd2f5e11.swf",
"obj_info_id": 71706.0,
"manifest": "https://images.neopets.com/cp/items/data/000/000/410/410722_3bcd2f5e11/manifest.json?v=1706"
}
}
}

View file

@ -0,0 +1,60 @@
{
"custom_pet": {
"name": "thyassa",
"owner": "donna",
"slot": 1.0,
"scale": 0.5,
"muted": false,
"body_id": 212.0,
"species_id": 7.0,
"color_id": 57.0,
"alt_style": false,
"alt_color": 57.0,
"style_closet_id": null,
"biology_by_zone": {
"37": {
"part_id": 10083.0,
"zone_id": 37.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/010/10083_8a1111a13f.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/010/10083_8a1111a13f/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"15": {
"part_id": 11613.0,
"zone_id": 15.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/011/11613_f7d8d377ab.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/011/11613_f7d8d377ab/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"34": {
"part_id": 14187.0,
"zone_id": 34.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/014/14187_0e65c2082f.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/014/14187_0e65c2082f/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"33": {
"part_id": 14189.0,
"zone_id": 33.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/014/14189_102e4991e9.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/014/14189_102e4991e9/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
}
},
"equipped_by_zone": [
],
"original_biology": [
]
},
"closet_items": [
],
"object_info_registry": [
],
"object_asset_registry": [
]
}

View file

@ -0,0 +1,50 @@
{
"custom_pet": {
"name": "thyassa",
"owner": "donna",
"slot": 1.0,
"scale": 0.5,
"muted": false,
"body_id": 212.0,
"species_id": 7.0,
"color_id": 57.0,
"alt_style": false,
"alt_color": 57.0,
"style_closet_id": null,
"biology_by_zone": {
"37": {
"part_id": 10083.0,
"zone_id": 37.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/010/10083_8a1111a13f.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/010/10083_8a1111a13f/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"15": {
"part_id": 11613.0,
"zone_id": 15.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/011/11613_f7d8d377ab.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/011/11613_f7d8d377ab/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"34": {
"part_id": 10448.0,
"zone_id": 34.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/010/10448_0b238e79e2.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/010/10448_0b238e79e2/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
},
"33": {
"part_id": 10451.0,
"zone_id": 33.0,
"asset_url": "https://images.neopets.com/cp/bio/swf/000/000/010/10451_cd4a8a8e47.swf",
"manifest": "https://images.neopets.com/cp/bio/data/000/000/010/10451_cd4a8a8e47/manifest.json",
"zones_restrict": "0000000000000000000000000000000000000000000000000000"
}
},
"equipped_by_zone": [],
"original_biology": []
},
"closet_items": [],
"object_info_registry": [],
"object_asset_registry": []
}

BIN
vendor/cache/rspec-core-3.13.2.gem vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/cache/rspec-mocks-3.13.2.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/rspec-rails-7.0.1.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/rspec-support-3.13.1.gem vendored Normal file

Binary file not shown.