split up migration to domain spec
This commit is contained in:
@@ -158,7 +158,11 @@ class Domain::MigrateToDomain
|
||||
logger.info "migrating fa followed users"
|
||||
Domain::User::FaUser
|
||||
.where(migrated_followed_users_at: nil)
|
||||
.find_each { |user| migrate_fa_user_followed_users(user) }
|
||||
.find_each do |user|
|
||||
ReduxApplicationRecord.transaction do
|
||||
migrate_fa_user_followed_users(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
@@ -520,7 +524,7 @@ class Domain::MigrateToDomain
|
||||
)
|
||||
end
|
||||
|
||||
if user.following_users.count != old_user.follows.count
|
||||
if new_user_ids.size != old_user.follows.count
|
||||
logger.error(
|
||||
"followers mismatch for #{user.name}: (#{user.following_users.count} != #{old_user.follows.count})",
|
||||
)
|
||||
|
||||
@@ -74,12 +74,22 @@ class Domain::Post < ReduxApplicationRecord
|
||||
source: :user
|
||||
end
|
||||
|
||||
sig { params(group_assoc_name: Symbol).void }
|
||||
def self.belongs_to_groups!(group_assoc_name: :groups)
|
||||
sig do
|
||||
params(
|
||||
group_assoc_name: Symbol,
|
||||
group_klass: T.class_of(Domain::PostGroup),
|
||||
join_klass: T.class_of(Domain::PostGroupJoin),
|
||||
).void
|
||||
end
|
||||
def self.belongs_to_groups!(group_assoc_name, group_klass, join_klass)
|
||||
has_many :post_group_joins,
|
||||
class_name: "::Domain::PostGroupJoin",
|
||||
class_name: join_klass.name,
|
||||
inverse_of: :post,
|
||||
dependent: :destroy
|
||||
has_many group_assoc_name, through: :post_group_joins, source: :group
|
||||
|
||||
has_many group_assoc_name,
|
||||
through: :post_group_joins,
|
||||
source: :group,
|
||||
class_name: group_klass.name
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,7 +33,9 @@ class Domain::Post::E621Post < Domain::Post
|
||||
|
||||
has_single_file!
|
||||
has_faving_users! Domain::User::E621User
|
||||
belongs_to_groups! group_assoc_name: :pools
|
||||
belongs_to_groups! :pools,
|
||||
Domain::PostGroup::E621Pool,
|
||||
Domain::PostGroupJoin::E621PoolJoin
|
||||
|
||||
belongs_to :parent_post,
|
||||
class_name: "Domain::Post::E621Post",
|
||||
|
||||
@@ -7,7 +7,9 @@ class Domain::Post::InkbunnyPost < Domain::Post
|
||||
|
||||
has_single_creator! Domain::User::InkbunnyUser
|
||||
has_faving_users! Domain::User::InkbunnyUser
|
||||
belongs_to_groups! group_assoc_name: :pools
|
||||
belongs_to_groups! :pools,
|
||||
Domain::PostGroup::InkbunnyPool,
|
||||
Domain::PostGroupJoin::InkbunnyPoolJoin
|
||||
|
||||
validates :ib_id, presence: true
|
||||
validates :state, presence: true, inclusion: { in: %w[ok error] }
|
||||
|
||||
@@ -45,8 +45,40 @@ class Domain::User < ReduxApplicationRecord
|
||||
|
||||
has_many :posts, through: :user_post_creations, source: :post
|
||||
has_many :faved_posts, through: :user_post_favs, source: :post
|
||||
has_many :following_users, through: :user_user_follows_to, source: :to
|
||||
has_many :followed_by_users, through: :user_user_follows_from, source: :from
|
||||
has_many :following_users, through: :user_user_follows_from, source: :to
|
||||
has_many :followed_by_users, through: :user_user_follows_to, source: :from
|
||||
|
||||
sig { params(klass: T.class_of(Domain::Post)).void }
|
||||
def self.has_created_posts!(klass)
|
||||
has_many :posts,
|
||||
through: :user_post_creations,
|
||||
source: :post,
|
||||
class_name: klass.name
|
||||
end
|
||||
|
||||
sig { params(klass: T.class_of(Domain::Post)).void }
|
||||
def self.has_faved_posts!(klass)
|
||||
has_many :faved_posts,
|
||||
through: :user_post_favs,
|
||||
source: :post,
|
||||
class_name: klass.name
|
||||
end
|
||||
|
||||
sig { params(klass: T.class_of(Domain::User)).void }
|
||||
def self.has_followed_users!(klass)
|
||||
has_many :following_users,
|
||||
through: :user_user_follows_from,
|
||||
source: :to,
|
||||
class_name: klass.name
|
||||
end
|
||||
|
||||
sig { params(klass: T.class_of(Domain::User)).void }
|
||||
def self.has_followed_by_users!(klass)
|
||||
has_many :followed_by_users,
|
||||
through: :user_user_follows_to,
|
||||
source: :from,
|
||||
class_name: klass.name
|
||||
end
|
||||
|
||||
has_one :avatar,
|
||||
-> { order(created_at: :desc) },
|
||||
|
||||
@@ -20,6 +20,8 @@ class Domain::User::E621User < Domain::User
|
||||
validates :e621_id, presence: true
|
||||
validates :name, length: { minimum: 1 }, allow_nil: false
|
||||
|
||||
has_faved_posts! Domain::Post::E621Post
|
||||
|
||||
sig { override.returns(T.nilable(String)) }
|
||||
def to_param
|
||||
"e621/#{e621_id}" if e621_id.present?
|
||||
|
||||
@@ -20,6 +20,11 @@ class Domain::User::FaUser < Domain::User
|
||||
validates :name, presence: true
|
||||
validates :url_name, presence: true
|
||||
|
||||
has_followed_users! Domain::User::FaUser
|
||||
has_followed_by_users! Domain::User::FaUser
|
||||
has_created_posts! Domain::Post::FaPost
|
||||
has_faved_posts! Domain::Post::FaPost
|
||||
|
||||
sig { override.returns(T.nilable(String)) }
|
||||
def to_param
|
||||
"fa/#{url_name}" if url_name.present?
|
||||
|
||||
8
sorbet/rbi/dsl/domain/post/e621_post.rbi
generated
8
sorbet/rbi/dsl/domain/post/e621_post.rbi
generated
@@ -548,10 +548,10 @@ class Domain::Post::E621Post
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::Post::E621Post` class because it declared `has_many :pools, through: :post_group_joins`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::PostGroup::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::PostGroup::E621Pool::PrivateCollectionProxy) }
|
||||
def pools; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroup]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroup::E621Pool]).void }
|
||||
def pools=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
@@ -562,10 +562,10 @@ class Domain::Post::E621Post
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::Post::E621Post` class because it declared `has_many :post_group_joins`.
|
||||
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
|
||||
sig { returns(::Domain::PostGroupJoin::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::PostGroupJoin::E621PoolJoin::PrivateCollectionProxy) }
|
||||
def post_group_joins; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroupJoin]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroupJoin::E621PoolJoin]).void }
|
||||
def post_group_joins=(value); end
|
||||
|
||||
sig { returns(T.nilable(::Domain::PostFile)) }
|
||||
|
||||
8
sorbet/rbi/dsl/domain/post/inkbunny_post.rbi
generated
8
sorbet/rbi/dsl/domain/post/inkbunny_post.rbi
generated
@@ -508,10 +508,10 @@ class Domain::Post::InkbunnyPost
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::Post::InkbunnyPost` class because it declared `has_many :pools, through: :post_group_joins`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::PostGroup::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::PostGroup::InkbunnyPool::PrivateCollectionProxy) }
|
||||
def pools; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroup]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroup::InkbunnyPool]).void }
|
||||
def pools=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
@@ -522,10 +522,10 @@ class Domain::Post::InkbunnyPost
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::Post::InkbunnyPost` class because it declared `has_many :post_group_joins`.
|
||||
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
|
||||
sig { returns(::Domain::PostGroupJoin::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::PostGroupJoin::InkbunnyPoolJoin::PrivateCollectionProxy) }
|
||||
def post_group_joins; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroupJoin]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::PostGroupJoin::InkbunnyPoolJoin]).void }
|
||||
def post_group_joins=(value); end
|
||||
|
||||
sig { returns(T.nilable(::Domain::UserPostCreation)) }
|
||||
|
||||
4
sorbet/rbi/dsl/domain/user.rbi
generated
4
sorbet/rbi/dsl/domain/user.rbi
generated
@@ -460,7 +460,7 @@ class Domain::User
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :followed_by_users, through: :user_user_follows_from`.
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :followed_by_users, through: :user_user_follows_to`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
def followed_by_users; end
|
||||
@@ -474,7 +474,7 @@ class Domain::User
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def following_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :following_users, through: :user_user_follows_to`.
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :following_users, through: :user_user_follows_from`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
def following_users; end
|
||||
|
||||
10
sorbet/rbi/dsl/domain/user/e621_user.rbi
generated
10
sorbet/rbi/dsl/domain/user/e621_user.rbi
generated
@@ -478,12 +478,12 @@ class Domain::User::E621User
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def faved_post_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :faved_posts, through: :user_post_favs`.
|
||||
# This method is created by ActiveRecord on the `Domain::User::E621User` class because it declared `has_many :faved_posts, through: :user_post_favs`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::Post::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::Post::E621Post::PrivateCollectionProxy) }
|
||||
def faved_posts; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::Post]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::Post::E621Post]).void }
|
||||
def faved_posts=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
@@ -492,7 +492,7 @@ class Domain::User::E621User
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :followed_by_users, through: :user_user_follows_from`.
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :followed_by_users, through: :user_user_follows_to`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
def followed_by_users; end
|
||||
@@ -506,7 +506,7 @@ class Domain::User::E621User
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def following_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :following_users, through: :user_user_follows_to`.
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :following_users, through: :user_user_follows_from`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
def following_users; end
|
||||
|
||||
24
sorbet/rbi/dsl/domain/user/fa_user.rbi
generated
24
sorbet/rbi/dsl/domain/user/fa_user.rbi
generated
@@ -478,12 +478,12 @@ class Domain::User::FaUser
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def faved_post_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :faved_posts, through: :user_post_favs`.
|
||||
# This method is created by ActiveRecord on the `Domain::User::FaUser` class because it declared `has_many :faved_posts, through: :user_post_favs`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::Post::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::Post::FaPost::PrivateCollectionProxy) }
|
||||
def faved_posts; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::Post]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::Post::FaPost]).void }
|
||||
def faved_posts=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
@@ -492,12 +492,12 @@ class Domain::User::FaUser
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :followed_by_users, through: :user_user_follows_from`.
|
||||
# This method is created by ActiveRecord on the `Domain::User::FaUser` class because it declared `has_many :followed_by_users, through: :user_user_follows_to`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::User::FaUser::PrivateCollectionProxy) }
|
||||
def followed_by_users; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::User]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::User::FaUser]).void }
|
||||
def followed_by_users=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
@@ -506,12 +506,12 @@ class Domain::User::FaUser
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def following_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :following_users, through: :user_user_follows_to`.
|
||||
# This method is created by ActiveRecord on the `Domain::User::FaUser` class because it declared `has_many :following_users, through: :user_user_follows_from`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::User::FaUser::PrivateCollectionProxy) }
|
||||
def following_users; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::User]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::User::FaUser]).void }
|
||||
def following_users=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
@@ -520,12 +520,12 @@ class Domain::User::FaUser
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def post_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :posts, through: :user_post_creations`.
|
||||
# This method is created by ActiveRecord on the `Domain::User::FaUser` class because it declared `has_many :posts, through: :user_post_creations`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::Post::PrivateCollectionProxy) }
|
||||
sig { returns(::Domain::Post::FaPost::PrivateCollectionProxy) }
|
||||
def posts; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::Post]).void }
|
||||
sig { params(value: T::Enumerable[::Domain::Post::FaPost]).void }
|
||||
def posts=(value); end
|
||||
|
||||
sig { returns(T.nilable(::Domain::UserAvatar)) }
|
||||
|
||||
4
sorbet/rbi/dsl/domain/user/inkbunny_user.rbi
generated
4
sorbet/rbi/dsl/domain/user/inkbunny_user.rbi
generated
@@ -527,7 +527,7 @@ class Domain::User::InkbunnyUser
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :followed_by_users, through: :user_user_follows_from`.
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :followed_by_users, through: :user_user_follows_to`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
def followed_by_users; end
|
||||
@@ -541,7 +541,7 @@ class Domain::User::InkbunnyUser
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def following_user_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :following_users, through: :user_user_follows_to`.
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :following_users, through: :user_user_follows_from`.
|
||||
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
|
||||
sig { returns(::Domain::User::PrivateCollectionProxy) }
|
||||
def following_users; end
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
FactoryBot.define do
|
||||
factory :domain_fa_user_avatar, class: "Domain::Fa::UserAvatar" do
|
||||
association :user, factory: :domain_fa_user
|
||||
association :file, factory: :http_log_entry
|
||||
state { :ok }
|
||||
state_detail { {} }
|
||||
file_url_str { "https://example.com/avatar.jpg" }
|
||||
|
||||
association :log_entry, factory: :http_log_entry
|
||||
after(:build) { |avatar| avatar.file = avatar.log_entry&.response }
|
||||
end
|
||||
end
|
||||
|
||||
210
spec/lib/domain/migrate_to_domain/migrate_e621_to_domain_spec.rb
Normal file
210
spec/lib/domain/migrate_to_domain/migrate_e621_to_domain_spec.rb
Normal file
@@ -0,0 +1,210 @@
|
||||
# typed: false
|
||||
require "rails_helper"
|
||||
require_relative "./shared_examples"
|
||||
|
||||
RSpec.describe Domain::MigrateToDomain do
|
||||
include_context "migrate_to_domain"
|
||||
|
||||
def expect_users_match(old_user, new_user)
|
||||
expect(new_user).to have_attributes(
|
||||
e621_id: old_user.e621_user_id,
|
||||
name: old_user.name,
|
||||
favs_are_hidden: old_user.favs_are_hidden,
|
||||
num_other_favs_cached: old_user.num_other_favs_cached,
|
||||
scanned_favs_status: old_user.scanned_favs_status,
|
||||
scanned_favs_at: be_within(1.second).of(old_user.scanned_favs_at),
|
||||
)
|
||||
end
|
||||
|
||||
def expect_posts_match(old_post, new_post)
|
||||
expect(new_post).to have_attributes(
|
||||
state: old_post.state,
|
||||
e621_id: old_post.e621_id,
|
||||
scanned_post_favs_at: old_post.scanned_post_favs_at,
|
||||
rating: old_post.rating,
|
||||
tags_array: old_post.tags_array,
|
||||
flags_array: old_post.flags_array,
|
||||
pools_array: old_post.pools_array,
|
||||
sources_array: old_post.sources_array,
|
||||
artists_array: old_post.artists_array,
|
||||
e621_updated_at: be_within(1.second).of(old_post.e621_updated_at),
|
||||
last_index_page_id: old_post.last_index_page_id,
|
||||
caused_by_entry_id: old_post.caused_by_entry_id,
|
||||
scan_log_entry_id: old_post.scan_log_entry_id,
|
||||
index_page_ids: old_post.index_page_ids,
|
||||
prev_md5s: old_post.prev_md5s,
|
||||
scan_error: old_post.scan_error,
|
||||
file_error: old_post.file_error,
|
||||
created_at: be_within(1.second).of(old_post.created_at),
|
||||
parent_post_e621_id: old_post.parent_e621_id,
|
||||
)
|
||||
end
|
||||
|
||||
describe "#migrate_e621_users" do
|
||||
let!(:old_user) do
|
||||
Domain::E621::User.create!(
|
||||
e621_user_id: 123,
|
||||
name: "test_user",
|
||||
favs_are_hidden: true,
|
||||
num_other_favs_cached: 42,
|
||||
scanned_favs_status: "ok",
|
||||
scanned_favs_at: Time.current,
|
||||
)
|
||||
end
|
||||
|
||||
it "migrates users that don't exist in the new table" do
|
||||
expect { migrator.migrate_e621_users }.to change(
|
||||
Domain::User::E621User,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
new_user = Domain::User::E621User.find_by(e621_id: old_user.e621_user_id)
|
||||
expect_users_match(old_user, new_user)
|
||||
end
|
||||
|
||||
it "skips users that already exist in the new table" do
|
||||
# Create a user in the new table first
|
||||
Domain::User::E621User.create!(
|
||||
e621_id: old_user.e621_user_id,
|
||||
name: old_user.name,
|
||||
)
|
||||
|
||||
expect { migrator.migrate_e621_users }.not_to change(
|
||||
Domain::User::E621User,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple users in batches" do
|
||||
# Create a few more old users
|
||||
additional_users =
|
||||
2.times.map do |i|
|
||||
Domain::E621::User.create!(
|
||||
e621_user_id: 456 + i,
|
||||
name: "test_user_#{i}",
|
||||
favs_are_hidden: false,
|
||||
num_other_favs_cached: i,
|
||||
scanned_favs_status: "ok",
|
||||
scanned_favs_at: Time.current,
|
||||
)
|
||||
end
|
||||
|
||||
expect { migrator.migrate_e621_users }.to change(
|
||||
Domain::User::E621User,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(Domain::User::E621User.count).to eq(3)
|
||||
expect(Domain::User::E621User.pluck(:e621_id)).to contain_exactly(
|
||||
123,
|
||||
456,
|
||||
457,
|
||||
)
|
||||
|
||||
# Verify all users were migrated correctly
|
||||
([old_user] + additional_users).each do |old_user|
|
||||
new_user =
|
||||
Domain::User::E621User.find_by(e621_id: old_user.e621_user_id)
|
||||
expect_users_match(old_user, new_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_e621_posts" do
|
||||
let!(:old_post) do
|
||||
Domain::E621::Post.create!(
|
||||
e621_id: 123,
|
||||
state: "ok",
|
||||
rating: "s",
|
||||
tags_array: {
|
||||
"general" => %w[tag1 tag2],
|
||||
},
|
||||
flags_array: ["flag1"],
|
||||
pools_array: ["pool1"],
|
||||
sources_array: ["source1"],
|
||||
artists_array: ["artist1"],
|
||||
e621_updated_at: Time.current,
|
||||
last_index_page_id: 1,
|
||||
caused_by_entry_id: 2,
|
||||
scan_log_entry_id: 3,
|
||||
index_page_ids: [1, 2, 3],
|
||||
prev_md5s: ["md5_1"],
|
||||
scan_error: nil,
|
||||
file_error: nil,
|
||||
parent_e621_id: nil,
|
||||
scanned_post_favs_at: Time.current,
|
||||
)
|
||||
end
|
||||
|
||||
it "migrates posts that don't exist in the new table" do
|
||||
expect { migrator.migrate_e621_posts }.to change(
|
||||
Domain::Post::E621Post,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
new_post = Domain::Post::E621Post.find_by(e621_id: old_post.e621_id)
|
||||
expect_posts_match(old_post, new_post)
|
||||
end
|
||||
|
||||
it "skips posts that already exist in the new table" do
|
||||
# Create a post in the new table first
|
||||
Domain::Post::E621Post.create!(
|
||||
e621_id: old_post.e621_id,
|
||||
state: "ok",
|
||||
rating: "q",
|
||||
)
|
||||
|
||||
expect { migrator.migrate_e621_posts }.not_to change(
|
||||
Domain::Post::E621Post,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple posts in batches" do
|
||||
# Create a few more old posts
|
||||
additional_posts =
|
||||
2.times.map do |i|
|
||||
Domain::E621::Post.create!(
|
||||
e621_id: 456 + i,
|
||||
state: "ok",
|
||||
rating: "q",
|
||||
tags_array: {
|
||||
"general" => ["tag#{i}"],
|
||||
},
|
||||
flags_array: ["flag#{i}"],
|
||||
pools_array: ["pool#{i}"],
|
||||
sources_array: ["source#{i}"],
|
||||
artists_array: ["artist#{i}"],
|
||||
e621_updated_at: Time.current,
|
||||
last_index_page_id: i,
|
||||
caused_by_entry_id: i + 1,
|
||||
scan_log_entry_id: i + 2,
|
||||
index_page_ids: [i],
|
||||
prev_md5s: ["md5_#{i}"],
|
||||
scan_error: nil,
|
||||
file_error: nil,
|
||||
parent_e621_id: nil,
|
||||
scanned_post_favs_at: Time.current,
|
||||
)
|
||||
end
|
||||
|
||||
expect { migrator.migrate_e621_posts }.to change(
|
||||
Domain::Post::E621Post,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(Domain::Post::E621Post.count).to eq(3)
|
||||
expect(Domain::Post::E621Post.pluck(:e621_id)).to contain_exactly(
|
||||
123,
|
||||
456,
|
||||
457,
|
||||
)
|
||||
|
||||
# Verify all posts were migrated correctly
|
||||
([old_post] + additional_posts).each do |old_post|
|
||||
new_post = Domain::Post::E621Post.find_by(e621_id: old_post.e621_id)
|
||||
expect_posts_match(old_post, new_post)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,495 @@
|
||||
# typed: false
|
||||
require "rails_helper"
|
||||
require_relative "./shared_examples"
|
||||
|
||||
RSpec.describe Domain::MigrateToDomain do
|
||||
include_context "migrate_to_domain"
|
||||
|
||||
def expect_fa_users_match(old_user, new_user)
|
||||
expect(new_user).to have_attributes(
|
||||
url_name: old_user.url_name,
|
||||
name: old_user.name,
|
||||
full_name: old_user.full_name,
|
||||
artist_type: old_user.artist_type,
|
||||
mood: old_user.mood,
|
||||
profile_html: old_user.profile_html,
|
||||
num_pageviews: old_user.num_pageviews,
|
||||
num_submissions: old_user.num_submissions,
|
||||
num_comments_recieved: old_user.num_comments_recieved,
|
||||
num_comments_given: old_user.num_comments_given,
|
||||
num_journals: old_user.num_journals,
|
||||
num_favorites: old_user.num_favorites,
|
||||
scanned_gallery_at: be_within(1.second).of(old_user.scanned_gallery_at),
|
||||
scanned_page_at: be_within(1.second).of(old_user.scanned_page_at),
|
||||
registered_at: be_within(1.second).of(old_user.registered_at),
|
||||
)
|
||||
|
||||
if old_user.avatar.present?
|
||||
expect(new_user.avatar).to have_attributes(
|
||||
log_entry_id: old_user.avatar.log_entry_id,
|
||||
url_str: old_user.avatar.file_url_str,
|
||||
state: old_user.avatar.state == "ok" ? "ok" : "error",
|
||||
)
|
||||
if old_user.avatar.state != "ok"
|
||||
expect(new_user.avatar.error_message).to eq(old_user.avatar.state)
|
||||
end
|
||||
else
|
||||
expect(new_user.avatar).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
def expect_fa_posts_match(old_post, new_post)
|
||||
expect(new_post).to have_attributes(
|
||||
state: old_post.state,
|
||||
title: old_post.title,
|
||||
fa_id: old_post.fa_id,
|
||||
category: old_post.category,
|
||||
theme: old_post.theme,
|
||||
species: old_post.species,
|
||||
gender: old_post.gender,
|
||||
description: old_post.description,
|
||||
keywords: old_post.keywords,
|
||||
num_favorites: old_post.num_favorites,
|
||||
num_comments: old_post.num_comments,
|
||||
num_views: old_post.num_views,
|
||||
posted_at: be_within(1.second).of(old_post.posted_at),
|
||||
scanned_at: be_within(1.second).of(old_post.scanned_at),
|
||||
scan_file_error: old_post.scan_file_error,
|
||||
last_user_page_id: old_post.last_user_page_id,
|
||||
last_submission_page_id: old_post.last_submission_page_id,
|
||||
first_browse_page_id: old_post.first_browse_page_id,
|
||||
first_gallery_page_id: old_post.first_gallery_page_id,
|
||||
first_seen_entry_id: old_post.first_seen_entry_id,
|
||||
created_at: be_within(1.second).of(old_post.created_at),
|
||||
)
|
||||
|
||||
if old_post.creator.present?
|
||||
expect(new_post.creator).to have_attributes(
|
||||
url_name: old_post.creator.url_name,
|
||||
)
|
||||
else
|
||||
expect(new_post.creator).to be_nil
|
||||
end
|
||||
|
||||
if old_post.file.present?
|
||||
expect(new_post.file).to have_attributes(
|
||||
log_entry_id: old_post.file_id,
|
||||
url_str: old_post.file_url_str,
|
||||
state: old_post.state,
|
||||
)
|
||||
else
|
||||
expect(new_post.file).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_fa_users" do
|
||||
let!(:old_user) do
|
||||
Domain::Fa::User.create!(
|
||||
url_name: "testuser",
|
||||
name: "Test_User",
|
||||
full_name: "Test User Full Name",
|
||||
artist_type: "artist",
|
||||
mood: "happy",
|
||||
profile_html: "<p>Test profile</p>",
|
||||
num_pageviews: 1000,
|
||||
num_submissions: 50,
|
||||
num_comments_recieved: 200,
|
||||
num_comments_given: 150,
|
||||
num_journals: 10,
|
||||
num_favorites: 300,
|
||||
scanned_gallery_at: Time.current,
|
||||
scanned_page_at: Time.current,
|
||||
registered_at: 1.year.ago,
|
||||
)
|
||||
end
|
||||
|
||||
it "migrates users that don't exist in the new table" do
|
||||
expect { migrator.migrate_fa_users }.to change(
|
||||
Domain::User::FaUser,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
new_user = Domain::User::FaUser.find_by(url_name: old_user.url_name)
|
||||
expect_fa_users_match(old_user, new_user)
|
||||
end
|
||||
|
||||
it "skips users that already exist in the new table" do
|
||||
# Create a user in the new table first
|
||||
Domain::User::FaUser.create!(
|
||||
url_name: old_user.url_name,
|
||||
name: old_user.name,
|
||||
)
|
||||
|
||||
expect { migrator.migrate_fa_users }.not_to change(
|
||||
Domain::User::FaUser,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple users in batches" do
|
||||
# Create a few more old users
|
||||
additional_users =
|
||||
2.times.map do |i|
|
||||
Domain::Fa::User.create!(
|
||||
url_name: "testuser#{i}",
|
||||
name: "Test_User_#{i}",
|
||||
full_name: "Test User #{i} Full Name",
|
||||
artist_type: "artist",
|
||||
mood: "happy",
|
||||
profile_html: "<p>Test profile #{i}</p>",
|
||||
num_pageviews: 1000 + i,
|
||||
num_submissions: 50 + i,
|
||||
num_comments_recieved: 200 + i,
|
||||
num_comments_given: 150 + i,
|
||||
num_journals: 10 + i,
|
||||
num_favorites: 300 + i,
|
||||
scanned_gallery_at: Time.current,
|
||||
scanned_page_at: Time.current,
|
||||
registered_at: i.days.ago,
|
||||
)
|
||||
end
|
||||
|
||||
expect { migrator.migrate_fa_users }.to change(
|
||||
Domain::User::FaUser,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(Domain::User::FaUser.count).to eq(3)
|
||||
expect(Domain::User::FaUser.pluck(:url_name)).to contain_exactly(
|
||||
"testuser",
|
||||
"testuser0",
|
||||
"testuser1",
|
||||
)
|
||||
|
||||
# Verify all users were migrated correctly
|
||||
([old_user] + additional_users).each do |old_user|
|
||||
new_user = Domain::User::FaUser.find_by(url_name: old_user.url_name)
|
||||
expect_fa_users_match(old_user, new_user)
|
||||
end
|
||||
end
|
||||
|
||||
it "handles users with avatars" do
|
||||
avatar = create(:domain_fa_user_avatar, user: old_user)
|
||||
old_user.avatar = avatar
|
||||
old_user.save!
|
||||
|
||||
migrator.migrate_fa_users
|
||||
|
||||
new_user = Domain::User::FaUser.find_by(url_name: old_user.url_name)
|
||||
expect_fa_users_match(old_user, new_user)
|
||||
expect(new_user.avatar).to be_present
|
||||
expect(new_user.avatar.url_str).to eq(avatar.file_url_str)
|
||||
expect(new_user.avatar.state).to eq("ok")
|
||||
end
|
||||
|
||||
it "handles users with errored avatars" do
|
||||
avatar =
|
||||
create(:domain_fa_user_avatar, state: "download_error", user: old_user)
|
||||
old_user.avatar = avatar
|
||||
old_user.save!
|
||||
|
||||
migrator.migrate_fa_users
|
||||
|
||||
new_user = Domain::User::FaUser.find_by(url_name: old_user.url_name)
|
||||
expect_fa_users_match(old_user, new_user)
|
||||
expect(new_user.avatar).to be_present
|
||||
expect(new_user.avatar.url_str).to eq(avatar.file_url_str)
|
||||
expect(new_user.avatar.state).to eq("error")
|
||||
expect(new_user.avatar.error_message).to eq("download_error")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_fa_posts" do
|
||||
let!(:creator) do
|
||||
Domain::Fa::User.create!(url_name: "artist1", name: "Artist 1")
|
||||
end
|
||||
|
||||
let!(:new_creator) do
|
||||
Domain::User::FaUser.create!(url_name: "artist1", name: "Artist 1")
|
||||
end
|
||||
|
||||
let!(:old_post) do
|
||||
Domain::Fa::Post.create!(
|
||||
fa_id: 123,
|
||||
state: "ok",
|
||||
title: "Test Post",
|
||||
category: "artwork",
|
||||
theme: "abstract",
|
||||
species: "canine",
|
||||
gender: "male",
|
||||
description: "Test description",
|
||||
keywords: %w[test art],
|
||||
num_favorites: 42,
|
||||
num_comments: 10,
|
||||
num_views: 100,
|
||||
posted_at: Time.current,
|
||||
scanned_at: Time.current,
|
||||
scan_file_error: nil,
|
||||
last_user_page_id: 1,
|
||||
last_submission_page_id: 2,
|
||||
first_browse_page_id: 3,
|
||||
first_gallery_page_id: 4,
|
||||
first_seen_entry_id: 5,
|
||||
creator: creator,
|
||||
file_url_str: "https://example.com/image.jpg",
|
||||
file: create(:http_log_entry),
|
||||
)
|
||||
end
|
||||
|
||||
it "migrates posts that don't exist in the new table" do
|
||||
expect { migrator.migrate_fa_posts }.to change(
|
||||
Domain::Post::FaPost,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
new_post = Domain::Post::FaPost.find_by(fa_id: old_post.fa_id)
|
||||
expect_fa_posts_match(old_post, new_post)
|
||||
end
|
||||
|
||||
it "skips posts that already exist in the new table" do
|
||||
# Create a post in the new table first
|
||||
Domain::Post::FaPost.create!(fa_id: old_post.fa_id, state: "ok")
|
||||
|
||||
expect { migrator.migrate_fa_posts }.not_to change(
|
||||
Domain::Post::FaPost,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple posts in batches" do
|
||||
# Create a few more old posts
|
||||
additional_posts =
|
||||
2.times.map do |i|
|
||||
Domain::Fa::Post.create!(
|
||||
fa_id: 456 + i,
|
||||
state: "ok",
|
||||
title: "Test Post #{i}",
|
||||
category: "artwork",
|
||||
theme: "abstract",
|
||||
species: "canine",
|
||||
gender: "male",
|
||||
description: "Test description #{i}",
|
||||
keywords: ["test#{i}", "art"],
|
||||
num_favorites: 42 + i,
|
||||
num_comments: 10 + i,
|
||||
num_views: 100 + i,
|
||||
posted_at: Time.current,
|
||||
scanned_at: Time.current,
|
||||
scan_file_error: nil,
|
||||
last_user_page_id: i + 1,
|
||||
last_submission_page_id: i + 2,
|
||||
first_browse_page_id: i + 3,
|
||||
first_gallery_page_id: i + 4,
|
||||
first_seen_entry_id: i + 5,
|
||||
creator: creator,
|
||||
file_url_str: "https://example.com/image_#{i}.jpg",
|
||||
file: create(:http_log_entry),
|
||||
)
|
||||
end
|
||||
|
||||
expect { migrator.migrate_fa_posts }.to change(
|
||||
Domain::Post::FaPost,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(Domain::Post::FaPost.count).to eq(3)
|
||||
expect(Domain::Post::FaPost.pluck(:fa_id)).to contain_exactly(
|
||||
123,
|
||||
456,
|
||||
457,
|
||||
)
|
||||
|
||||
# Verify all posts were migrated correctly
|
||||
([old_post] + additional_posts).each do |old_post|
|
||||
new_post = Domain::Post::FaPost.find_by(fa_id: old_post.fa_id)
|
||||
expect_fa_posts_match(old_post, new_post)
|
||||
end
|
||||
end
|
||||
|
||||
it "handles posts without creators" do
|
||||
post_without_creator =
|
||||
Domain::Fa::Post.create!(
|
||||
fa_id: 789,
|
||||
state: "ok",
|
||||
title: "No Creator Post",
|
||||
category: "artwork",
|
||||
posted_at: Time.current,
|
||||
scanned_at: Time.current,
|
||||
)
|
||||
|
||||
expect { migrator.migrate_fa_posts }.to change(
|
||||
Domain::Post::FaPost,
|
||||
:count,
|
||||
).by(2)
|
||||
|
||||
new_post = Domain::Post::FaPost.find_by(fa_id: post_without_creator.fa_id)
|
||||
expect_fa_posts_match(post_without_creator, new_post)
|
||||
end
|
||||
|
||||
it "handles posts without files" do
|
||||
post_without_file =
|
||||
Domain::Fa::Post.create!(
|
||||
fa_id: 789,
|
||||
state: "ok",
|
||||
title: "No File Post",
|
||||
category: "artwork",
|
||||
posted_at: Time.current,
|
||||
scanned_at: Time.current,
|
||||
creator: creator,
|
||||
)
|
||||
|
||||
expect { migrator.migrate_fa_posts }.to change(
|
||||
Domain::Post::FaPost,
|
||||
:count,
|
||||
).by(2)
|
||||
|
||||
new_post = Domain::Post::FaPost.find_by(fa_id: post_without_file.fa_id)
|
||||
expect_fa_posts_match(post_without_file, new_post)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_fa_users_favs" do
|
||||
let!(:old_user) do
|
||||
Domain::Fa::User.create!(url_name: "testuser", name: "Test_User")
|
||||
end
|
||||
|
||||
let!(:new_user) do
|
||||
Domain::User::FaUser.create!(url_name: "testuser", name: "Test_User")
|
||||
end
|
||||
|
||||
let!(:old_posts) do
|
||||
3.times.map do |i|
|
||||
Domain::Fa::Post.create!(
|
||||
fa_id: i + 1,
|
||||
state: "ok",
|
||||
title: "Test Post #{i}",
|
||||
category: "artwork",
|
||||
posted_at: Time.current,
|
||||
scanned_at: Time.current,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
let!(:new_posts) do
|
||||
old_posts.map do |old_post|
|
||||
Domain::Post::FaPost.create!(
|
||||
fa_id: old_post.fa_id,
|
||||
state: "ok",
|
||||
title: old_post.title,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
# Add favs to the old user
|
||||
old_user.fav_posts = old_posts
|
||||
old_user.save!
|
||||
end
|
||||
|
||||
it "migrates user favs correctly" do
|
||||
expect { migrator.migrate_fa_users_favs }.to change(
|
||||
Domain::UserPostFav,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
new_user.reload
|
||||
expect(new_user.faved_posts.count).to eq(3)
|
||||
expect(new_user.faved_posts.pluck(:fa_id)).to contain_exactly(1, 2, 3)
|
||||
expect(new_user.migrated_user_favs_at).to be_present
|
||||
end
|
||||
|
||||
it "skips users that have already been migrated" do
|
||||
new_user.update!(migrated_user_favs_at: Time.current)
|
||||
|
||||
expect { migrator.migrate_fa_users_favs }.not_to change(
|
||||
Domain::UserPostFav,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles missing posts gracefully" do
|
||||
# Delete one of the new posts to simulate a missing post
|
||||
new_posts.first.destroy
|
||||
|
||||
expect { migrator.migrate_fa_users_favs }.to change(
|
||||
Domain::UserPostFav,
|
||||
:count,
|
||||
).by(2)
|
||||
|
||||
expect(new_user.faved_posts.count).to eq(2)
|
||||
expect(new_user.migrated_user_favs_at).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_fa_users_followed_users" do
|
||||
let!(:old_user) do
|
||||
Domain::Fa::User.create!(url_name: "testuser", name: "Test_User")
|
||||
end
|
||||
|
||||
let!(:new_user) do
|
||||
Domain::User::FaUser.create!(url_name: "testuser", name: "Test_User")
|
||||
end
|
||||
|
||||
let!(:old_followed_users) do
|
||||
3.times.map do |i|
|
||||
Domain::Fa::User.create!(
|
||||
url_name: "followeduser#{i}",
|
||||
name: "Followed User #{i}",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
let!(:new_followed_users) do
|
||||
old_followed_users.map do |old_followed_user|
|
||||
Domain::User::FaUser.create!(
|
||||
url_name: old_followed_user.url_name,
|
||||
name: old_followed_user.name,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
# Add follows to the old user
|
||||
old_user.follows = old_followed_users
|
||||
old_user.save!
|
||||
end
|
||||
|
||||
it "migrates user follows correctly" do
|
||||
expect { migrator.migrate_fa_users_followed_users }.to change(
|
||||
Domain::UserUserFollow,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(new_user.following_users.count).to eq(3)
|
||||
new_user.reload
|
||||
expect(new_user.following_users.pluck(:url_name)).to contain_exactly(
|
||||
"followeduser0",
|
||||
"followeduser1",
|
||||
"followeduser2",
|
||||
)
|
||||
expect(new_user.migrated_followed_users_at).to be_present
|
||||
end
|
||||
|
||||
it "skips users that have already been migrated" do
|
||||
new_user.update!(migrated_followed_users_at: Time.current)
|
||||
|
||||
expect { migrator.migrate_fa_users_followed_users }.not_to change(
|
||||
Domain::UserUserFollow,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles missing followed users gracefully" do
|
||||
# Delete one of the new followed users to simulate a missing user
|
||||
new_followed_users.first.destroy
|
||||
|
||||
expect { migrator.migrate_fa_users_followed_users }.to change(
|
||||
Domain::UserUserFollow,
|
||||
:count,
|
||||
).by(2)
|
||||
|
||||
expect(new_user.following_users.count).to eq(2)
|
||||
expect(new_user.migrated_followed_users_at).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,504 @@
|
||||
# typed: false
|
||||
require "rails_helper"
|
||||
require_relative "./shared_examples"
|
||||
|
||||
RSpec.describe Domain::MigrateToDomain do
|
||||
include_context "migrate_to_domain"
|
||||
|
||||
def expect_inkbunny_users_match(old_user, new_user)
|
||||
expect(new_user).to have_attributes(
|
||||
ib_id: old_user.ib_user_id,
|
||||
name: old_user.name,
|
||||
state: old_user.state.to_s,
|
||||
scanned_gallery_at:
|
||||
be_within(1.second).of(old_user.scanned_gallery_at) || be_nil,
|
||||
deep_update_log_entry_id: old_user.deep_update_log_entry_id,
|
||||
shallow_update_log_entry_id: old_user.shallow_update_log_entry_id,
|
||||
created_at: be_within(1.second).of(old_user.created_at),
|
||||
)
|
||||
|
||||
if old_user.avatar_log_entry.present?
|
||||
expect(new_user.avatar).to have_attributes(
|
||||
log_entry_id: old_user.avatar_log_entry.id,
|
||||
url_str: old_user.avatar_log_entry.uri_str,
|
||||
state: old_user.avatar_log_entry.status_code == 200 ? "ok" : "error",
|
||||
)
|
||||
else
|
||||
expect(new_user.avatar).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
def expect_inkbunny_posts_match(old_post, new_post)
|
||||
expect(new_post).to have_attributes(
|
||||
state: old_post.state,
|
||||
ib_id: old_post.ib_post_id,
|
||||
rating: old_post.rating,
|
||||
submission_type: old_post.submission_type,
|
||||
created_at: be_within(1.second).of(old_post.created_at),
|
||||
)
|
||||
|
||||
if old_post.creator.present?
|
||||
expect(new_post.creator).to have_attributes(
|
||||
ib_id: old_post.creator.ib_user_id,
|
||||
)
|
||||
else
|
||||
expect(new_post.creator).to be_nil
|
||||
end
|
||||
|
||||
if old_post.files.present?
|
||||
expect(new_post.files.map(&:log_entry_id)).to match_array(
|
||||
old_post.files.map(&:log_entry_id),
|
||||
)
|
||||
expect(new_post.files.map(&:url_str)).to match_array(
|
||||
old_post.files.map(&:url_str),
|
||||
)
|
||||
expect(new_post.files.map(&:state)).to match_array(
|
||||
old_post.files.map(&:state),
|
||||
)
|
||||
else
|
||||
expect(new_post.files).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
def expect_inkbunny_pools_match(old_pool, new_pool)
|
||||
expect(new_pool).to have_attributes(ib_id: old_pool.ib_pool_id)
|
||||
|
||||
if old_pool.pool_joins.present?
|
||||
expect(new_pool.post_group_joins.count).to eq(old_pool.pool_joins.count)
|
||||
|
||||
old_pool.pool_joins.each_with_index do |old_join, i|
|
||||
new_join = new_pool.post_group_joins[i]
|
||||
|
||||
expect(new_join.post.ib_id).to eq(old_join.post.ib_post_id)
|
||||
|
||||
if old_join.left_post.present?
|
||||
expect(new_join.left_post.ib_id).to eq(old_join.left_post.ib_post_id)
|
||||
else
|
||||
expect(new_join.left_post).to be_nil
|
||||
end
|
||||
|
||||
if old_join.right_post.present?
|
||||
expect(new_join.right_post.ib_id).to eq(
|
||||
old_join.right_post.ib_post_id,
|
||||
)
|
||||
else
|
||||
expect(new_join.right_post).to be_nil
|
||||
end
|
||||
end
|
||||
else
|
||||
expect(new_pool.post_group_joins).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_inkbunny_users" do
|
||||
let!(:old_user) do
|
||||
create(
|
||||
:domain_inkbunny_user,
|
||||
name: "test_user",
|
||||
ib_user_id: 123,
|
||||
state: :ok,
|
||||
state_detail: {
|
||||
},
|
||||
scanned_gallery_at: Time.current,
|
||||
deep_update_log_entry: create(:http_log_entry),
|
||||
shallow_update_log_entry: create(:http_log_entry),
|
||||
)
|
||||
end
|
||||
|
||||
it "migrates users that don't exist in the new table" do
|
||||
expect { migrator.migrate_inkbunny_users }.to change(
|
||||
Domain::User::InkbunnyUser,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
new_user = Domain::User::InkbunnyUser.find_by(ib_id: old_user.ib_user_id)
|
||||
expect_inkbunny_users_match(old_user, new_user)
|
||||
end
|
||||
|
||||
it "skips users that already exist in the new table" do
|
||||
# Create a user in the new table first
|
||||
Domain::User::InkbunnyUser.create!(
|
||||
ib_id: old_user.ib_user_id,
|
||||
name: old_user.name,
|
||||
state: "ok",
|
||||
)
|
||||
|
||||
expect { migrator.migrate_inkbunny_users }.not_to change(
|
||||
Domain::User::InkbunnyUser,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple users in batches" do
|
||||
# Create a few more old users
|
||||
additional_users =
|
||||
2.times.map do |i|
|
||||
create(
|
||||
:domain_inkbunny_user,
|
||||
name: "test_user_#{i}",
|
||||
ib_user_id: 456 + i,
|
||||
scanned_gallery_at: Time.current,
|
||||
deep_update_log_entry: create(:http_log_entry),
|
||||
shallow_update_log_entry: create(:http_log_entry),
|
||||
)
|
||||
end
|
||||
|
||||
expect { migrator.migrate_inkbunny_users }.to change(
|
||||
Domain::User::InkbunnyUser,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(Domain::User::InkbunnyUser.count).to eq(3)
|
||||
expect(Domain::User::InkbunnyUser.pluck(:ib_id)).to contain_exactly(
|
||||
123,
|
||||
456,
|
||||
457,
|
||||
)
|
||||
|
||||
# Verify all users were migrated correctly
|
||||
([old_user] + additional_users).each do |old_user|
|
||||
new_user =
|
||||
Domain::User::InkbunnyUser.find_by(ib_id: old_user.ib_user_id)
|
||||
expect_inkbunny_users_match(old_user, new_user)
|
||||
end
|
||||
end
|
||||
|
||||
it "handles users with avatars" do
|
||||
avatar_log_entry =
|
||||
create(
|
||||
:http_log_entry,
|
||||
uri_str: "https://example.com/avatar.jpg",
|
||||
status_code: 200,
|
||||
)
|
||||
old_user.avatar_log_entry = avatar_log_entry
|
||||
old_user.save!
|
||||
|
||||
migrator.migrate_inkbunny_users
|
||||
|
||||
new_user = Domain::User::InkbunnyUser.find_by(ib_id: old_user.ib_user_id)
|
||||
expect_inkbunny_users_match(old_user, new_user)
|
||||
expect(new_user.avatar).to be_present
|
||||
expect(new_user.avatar.url_str).to eq(avatar_log_entry.uri_str)
|
||||
expect(new_user.avatar.state).to eq("ok")
|
||||
end
|
||||
|
||||
it "handles users with errored avatars" do
|
||||
avatar_log_entry =
|
||||
create(
|
||||
:http_log_entry,
|
||||
uri_str: "https://example.com/avatar.jpg",
|
||||
status_code: 404,
|
||||
)
|
||||
old_user.avatar_log_entry = avatar_log_entry
|
||||
old_user.save!
|
||||
|
||||
migrator.migrate_inkbunny_users
|
||||
|
||||
new_user = Domain::User::InkbunnyUser.find_by(ib_id: old_user.ib_user_id)
|
||||
expect_inkbunny_users_match(old_user, new_user)
|
||||
expect(new_user.avatar).to be_present
|
||||
expect(new_user.avatar.url_str).to eq(avatar_log_entry.uri_str)
|
||||
expect(new_user.avatar.state).to eq("error")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_inkbunny_posts" do
|
||||
let!(:creator) do
|
||||
Domain::Inkbunny::User.create!(
|
||||
name: "artist1",
|
||||
ib_user_id: 123,
|
||||
state: :ok,
|
||||
state_detail: {
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let!(:new_creator) do
|
||||
Domain::User::InkbunnyUser.create!(
|
||||
name: "artist1",
|
||||
ib_id: 123,
|
||||
state: "ok",
|
||||
)
|
||||
end
|
||||
|
||||
let!(:old_post) do
|
||||
post = create(:domain_inkbunny_post, creator: creator)
|
||||
file = create(:domain_inkbunny_file, post: post)
|
||||
post.files << file
|
||||
post
|
||||
end
|
||||
|
||||
it "migrates posts that don't exist in the new table" do
|
||||
expect { migrator.migrate_inkbunny_posts }.to change(
|
||||
Domain::Post::InkbunnyPost,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
new_post = Domain::Post::InkbunnyPost.find_by(ib_id: old_post.ib_post_id)
|
||||
expect_inkbunny_posts_match(old_post, new_post)
|
||||
end
|
||||
|
||||
it "skips posts that already exist in the new table" do
|
||||
# Create a post in the new table first
|
||||
Domain::Post::InkbunnyPost.create!(
|
||||
ib_id: old_post.ib_post_id,
|
||||
state: "ok",
|
||||
rating: "general",
|
||||
submission_type: "picture_pinup",
|
||||
)
|
||||
|
||||
expect { migrator.migrate_inkbunny_posts }.not_to change(
|
||||
Domain::Post::InkbunnyPost,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple posts in batches" do
|
||||
# Create a few more old posts
|
||||
additional_posts =
|
||||
2.times.map do |i|
|
||||
post = create(:domain_inkbunny_post, creator: creator)
|
||||
file = create(:domain_inkbunny_file, post: post)
|
||||
post.files << file
|
||||
post
|
||||
end
|
||||
|
||||
expect { migrator.migrate_inkbunny_posts }.to change(
|
||||
Domain::Post::InkbunnyPost,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(Domain::Post::InkbunnyPost.count).to eq(3)
|
||||
expect(Domain::Post::InkbunnyPost.pluck(:ib_id)).to contain_exactly(
|
||||
old_post.ib_post_id,
|
||||
additional_posts[0].ib_post_id,
|
||||
additional_posts[1].ib_post_id,
|
||||
)
|
||||
|
||||
# Verify all posts were migrated correctly
|
||||
([old_post] + additional_posts).each do |old_post|
|
||||
new_post =
|
||||
Domain::Post::InkbunnyPost.find_by(ib_id: old_post.ib_post_id)
|
||||
expect_inkbunny_posts_match(old_post, new_post)
|
||||
end
|
||||
end
|
||||
|
||||
it "handles posts without files" do
|
||||
post_without_files =
|
||||
Domain::Inkbunny::Post.create!(
|
||||
ib_post_id: 999,
|
||||
state: :ok,
|
||||
state_detail: {
|
||||
},
|
||||
rating: :general,
|
||||
submission_type: :picture_pinup,
|
||||
creator: creator,
|
||||
)
|
||||
|
||||
expect { migrator.migrate_inkbunny_posts }.to change(
|
||||
Domain::Post::InkbunnyPost,
|
||||
:count,
|
||||
).by(2)
|
||||
|
||||
new_post =
|
||||
Domain::Post::InkbunnyPost.find_by(ib_id: post_without_files.ib_post_id)
|
||||
expect_inkbunny_posts_match(post_without_files, new_post)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#migrate_inkbunny_pools" do
|
||||
let!(:creator) do
|
||||
Domain::Inkbunny::User.create!(
|
||||
name: "artist1",
|
||||
ib_user_id: 123,
|
||||
state: :ok,
|
||||
state_detail: {
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let!(:new_creator) do
|
||||
Domain::User::InkbunnyUser.create!(
|
||||
name: "artist1",
|
||||
ib_id: 123,
|
||||
state: "ok",
|
||||
)
|
||||
end
|
||||
|
||||
let!(:old_post1) do
|
||||
post = create(:domain_inkbunny_post, creator: creator, ib_post_id: 1)
|
||||
file = create(:domain_inkbunny_file, post: post)
|
||||
post.files << file
|
||||
post
|
||||
end
|
||||
|
||||
let!(:old_post2) do
|
||||
post = create(:domain_inkbunny_post, creator: creator, ib_post_id: 2)
|
||||
file = create(:domain_inkbunny_file, post: post)
|
||||
post.files << file
|
||||
post
|
||||
end
|
||||
|
||||
let!(:old_post3) do
|
||||
post = create(:domain_inkbunny_post, creator: creator, ib_post_id: 3)
|
||||
file = create(:domain_inkbunny_file, post: post)
|
||||
post.files << file
|
||||
post
|
||||
end
|
||||
|
||||
let!(:new_post1) do
|
||||
Domain::Post::InkbunnyPost.create!(
|
||||
ib_id: old_post1.ib_post_id,
|
||||
state: "ok",
|
||||
rating: "general",
|
||||
submission_type: "picture_pinup",
|
||||
)
|
||||
end
|
||||
|
||||
let!(:new_post2) do
|
||||
Domain::Post::InkbunnyPost.create!(
|
||||
ib_id: old_post2.ib_post_id,
|
||||
state: "ok",
|
||||
rating: "general",
|
||||
submission_type: "picture_pinup",
|
||||
)
|
||||
end
|
||||
|
||||
let!(:new_post3) do
|
||||
Domain::Post::InkbunnyPost.create!(
|
||||
ib_id: old_post3.ib_post_id,
|
||||
state: "ok",
|
||||
rating: "general",
|
||||
submission_type: "picture_pinup",
|
||||
)
|
||||
end
|
||||
|
||||
let!(:old_pool) do
|
||||
pool = create(:domain_inkbunny_pool, ib_pool_id: 123)
|
||||
|
||||
# Create pool joins with sequential posts
|
||||
pool.pool_joins.create!(
|
||||
post: old_post1,
|
||||
left_post: nil,
|
||||
right_post: old_post2,
|
||||
)
|
||||
|
||||
pool.pool_joins.create!(
|
||||
post: old_post2,
|
||||
left_post: old_post1,
|
||||
right_post: old_post3,
|
||||
)
|
||||
|
||||
pool.pool_joins.create!(
|
||||
post: old_post3,
|
||||
left_post: old_post2,
|
||||
right_post: nil,
|
||||
)
|
||||
|
||||
pool
|
||||
end
|
||||
|
||||
it "migrates pools that don't exist in the new table" do
|
||||
expect { migrator.migrate_inkbunny_pools }.to change(
|
||||
Domain::PostGroup::InkbunnyPool,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
new_pool =
|
||||
Domain::PostGroup::InkbunnyPool.find_by(ib_id: old_pool.ib_pool_id)
|
||||
expect_inkbunny_pools_match(old_pool, new_pool)
|
||||
end
|
||||
|
||||
it "skips pools that already exist in the new table" do
|
||||
# Create a pool in the new table first
|
||||
Domain::PostGroup::InkbunnyPool.create!(ib_id: old_pool.ib_pool_id)
|
||||
|
||||
expect { migrator.migrate_inkbunny_pools }.not_to change(
|
||||
Domain::PostGroup::InkbunnyPool,
|
||||
:count,
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple pools in batches" do
|
||||
# Create a few more old pools
|
||||
additional_pools =
|
||||
2.times.map do |i|
|
||||
pool = create(:domain_inkbunny_pool, ib_pool_id: 456 + i)
|
||||
|
||||
# Add some joins to each pool
|
||||
pool.pool_joins.create!(
|
||||
post: old_post1,
|
||||
left_post: nil,
|
||||
right_post: old_post2,
|
||||
)
|
||||
|
||||
pool.pool_joins.create!(
|
||||
post: old_post2,
|
||||
left_post: old_post1,
|
||||
right_post: nil,
|
||||
)
|
||||
|
||||
pool
|
||||
end
|
||||
|
||||
expect { migrator.migrate_inkbunny_pools }.to change(
|
||||
Domain::PostGroup::InkbunnyPool,
|
||||
:count,
|
||||
).by(3)
|
||||
|
||||
expect(Domain::PostGroup::InkbunnyPool.count).to eq(3)
|
||||
expect(Domain::PostGroup::InkbunnyPool.pluck(:ib_id)).to contain_exactly(
|
||||
123,
|
||||
456,
|
||||
457,
|
||||
)
|
||||
|
||||
# Verify all pools were migrated correctly
|
||||
([old_pool] + additional_pools).each do |old_pool|
|
||||
new_pool =
|
||||
Domain::PostGroup::InkbunnyPool.find_by(ib_id: old_pool.ib_pool_id)
|
||||
expect_inkbunny_pools_match(old_pool, new_pool)
|
||||
end
|
||||
end
|
||||
|
||||
it "handles pools without joins" do
|
||||
pool_without_joins = create(:domain_inkbunny_pool, ib_pool_id: 789)
|
||||
|
||||
expect { migrator.migrate_inkbunny_pools }.to change(
|
||||
Domain::PostGroup::InkbunnyPool,
|
||||
:count,
|
||||
).by(2)
|
||||
|
||||
new_pool =
|
||||
Domain::PostGroup::InkbunnyPool.find_by(
|
||||
ib_id: pool_without_joins.ib_pool_id,
|
||||
)
|
||||
expect_inkbunny_pools_match(pool_without_joins, new_pool)
|
||||
end
|
||||
|
||||
it "maintains the correct post relationships in pool joins" do
|
||||
migrator.migrate_inkbunny_pools
|
||||
|
||||
new_pool =
|
||||
Domain::PostGroup::InkbunnyPool.find_by(ib_id: old_pool.ib_pool_id)
|
||||
|
||||
# Verify the sequence of posts is maintained
|
||||
joins = new_pool.post_group_joins.sort_by { |j| j.post.ib_id }
|
||||
|
||||
# First join should link to second post
|
||||
expect(joins[0].post.ib_id).to eq(old_post1.ib_post_id)
|
||||
expect(joins[0].left_post).to be_nil
|
||||
expect(joins[0].right_post.ib_id).to eq(old_post2.ib_post_id)
|
||||
|
||||
# Middle join should link to both first and third posts
|
||||
expect(joins[1].post.ib_id).to eq(old_post2.ib_post_id)
|
||||
expect(joins[1].left_post.ib_id).to eq(old_post1.ib_post_id)
|
||||
expect(joins[1].right_post.ib_id).to eq(old_post3.ib_post_id)
|
||||
|
||||
# Last join should link to second post
|
||||
expect(joins[2].post.ib_id).to eq(old_post3.ib_post_id)
|
||||
expect(joins[2].left_post.ib_id).to eq(old_post2.ib_post_id)
|
||||
expect(joins[2].right_post).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
7
spec/lib/domain/migrate_to_domain/shared_examples.rb
Normal file
7
spec/lib/domain/migrate_to_domain/shared_examples.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# typed: false
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.shared_context "migrate_to_domain" do
|
||||
# sink to /dev/null
|
||||
let(:migrator) { Domain::MigrateToDomain.new(File.open("/dev/null", "w")) }
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user