add specs for new exporters
This commit is contained in:
parent
4229c7f579
commit
2b3e7ab609
|
@ -39,7 +39,12 @@ class Exporter
|
|||
Sentry.capture_exception(e)
|
||||
@user.export_processing = false
|
||||
@user.save validate: false
|
||||
raise # so that e.g. the sidekiq job fails
|
||||
|
||||
# delete zipfile on errors
|
||||
@zipfile.close # so it gets written to disk first
|
||||
File.delete(@zipfile.name)
|
||||
|
||||
raise # let e.g. the sidekiq job fail so it's retryable later
|
||||
ensure
|
||||
@zipfile.close
|
||||
end
|
||||
|
|
|
@ -68,7 +68,12 @@ module UseCase
|
|||
picture.versions.each do |version, file|
|
||||
export_filename = "pictures/#{file.mounted_as}_#{version}_#{file.filename}"
|
||||
to[export_filename] = if file.url.start_with?("/")
|
||||
Rails.public_path.join(file.url.sub(%r{\A/+}, "")).read rescue "ceci n'est pas un image" # TODO: fix this
|
||||
begin
|
||||
Rails.public_path.join(file.url.sub(%r{\A/+}, "")).read
|
||||
rescue
|
||||
# TODO: fix image handling in local development environments!!! see #822
|
||||
"ceci n'est pas un image\n"
|
||||
end
|
||||
else
|
||||
HTTParty.get(file.url).parsed_response
|
||||
end
|
||||
|
|
|
@ -1,326 +1,118 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
require "support/example_exporter"
|
||||
require "base64"
|
||||
|
||||
require "exporter"
|
||||
|
||||
# This only tests the exporter itself to make sure zip file creation works.
|
||||
RSpec.describe Exporter do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
let(:user_params) do
|
||||
{
|
||||
answered_count: 144,
|
||||
asked_count: 72,
|
||||
comment_smiled_count: 15,
|
||||
commented_count: 12,
|
||||
confirmation_sent_at: 2.weeks.ago.utc,
|
||||
confirmed_at: 2.weeks.ago.utc + 1.hour,
|
||||
created_at: 2.weeks.ago.utc,
|
||||
current_sign_in_at: 8.hours.ago.utc,
|
||||
current_sign_in_ip: "198.51.100.220",
|
||||
last_sign_in_at: 1.hour.ago,
|
||||
last_sign_in_ip: "192.0.2.14",
|
||||
locale: "en",
|
||||
privacy_allow_anonymous_questions: true,
|
||||
privacy_allow_public_timeline: false,
|
||||
privacy_allow_stranger_answers: false,
|
||||
privacy_show_in_search: true,
|
||||
screen_name: "fizzyraccoon",
|
||||
show_foreign_themes: true,
|
||||
sign_in_count: 10,
|
||||
smiled_count: 28,
|
||||
profile: {
|
||||
display_name: "Fizzy Raccoon",
|
||||
description: "A small raccoon",
|
||||
location: "Binland",
|
||||
motivation_header: "",
|
||||
website: "https://retrospring.net"
|
||||
}
|
||||
}
|
||||
end
|
||||
let(:user) { FactoryBot.create(:user, **user_params) }
|
||||
let(:user) { FactoryBot.create(:user, screen_name: "fizzyraccoon", export_processing: true) }
|
||||
let(:instance) { described_class.new(user) }
|
||||
let(:zipfile_deletion_expected) { false }
|
||||
|
||||
before do
|
||||
stub_const("APP_CONFIG", {
|
||||
"hostname" => "example.com",
|
||||
"https" => true,
|
||||
"items_per_page" => 5,
|
||||
"fog" => {}
|
||||
})
|
||||
"site_name" => "justask",
|
||||
"hostname" => "example.com",
|
||||
"https" => true,
|
||||
"items_per_page" => 5,
|
||||
"fog" => {}
|
||||
}.with_indifferent_access)
|
||||
end
|
||||
|
||||
after do
|
||||
filename = instance.instance_variable_get(:@export_dirname)
|
||||
FileUtils.rm_r(filename) if File.exist?(filename)
|
||||
end
|
||||
|
||||
describe "#collect_user_info" do
|
||||
subject { instance.send(:collect_user_info) }
|
||||
|
||||
it "collects user info" do
|
||||
subject
|
||||
expect(instance.instance_variable_get(:@obj)).to eq(user_params.merge({
|
||||
administrator: false,
|
||||
moderator: false,
|
||||
id: user.id,
|
||||
updated_at: user.updated_at,
|
||||
profile_header: user.profile_header,
|
||||
profile_header_file_name: nil,
|
||||
profile_header_h: nil,
|
||||
profile_header_w: nil,
|
||||
profile_header_x: nil,
|
||||
profile_header_y: nil,
|
||||
profile_picture_file_name: nil,
|
||||
profile_picture_h: nil,
|
||||
profile_picture_w: nil,
|
||||
profile_picture_x: nil,
|
||||
profile_picture_y: nil
|
||||
}))
|
||||
filename = instance.instance_variable_get(:@zipfile)&.name
|
||||
unless File.exist?(filename)
|
||||
warn "exporter_spec.rb: wanted to clean up #{filename.inspect} but it does not exist!" unless zipfile_deletion_expected
|
||||
next
|
||||
end
|
||||
FileUtils.rm_r(filename)
|
||||
end
|
||||
|
||||
describe "#collect_questions" do
|
||||
subject { instance.send(:collect_questions) }
|
||||
describe "#export" do
|
||||
let(:export_name) { instance.instance_variable_get(:@export_name) }
|
||||
|
||||
context "exporting a user with several questions" do
|
||||
let!(:questions) { FactoryBot.create_list(:question, 25, user: user) }
|
||||
|
||||
it "collects questions" do
|
||||
subject
|
||||
expect(instance.instance_variable_get(:@obj)[:questions]).to eq(questions.map do |q|
|
||||
{
|
||||
answer_count: 0,
|
||||
answers: [],
|
||||
author_is_anonymous: q.author_is_anonymous,
|
||||
content: q.content,
|
||||
created_at: q.reload.created_at,
|
||||
id: q.id
|
||||
}
|
||||
end)
|
||||
subject do
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) do
|
||||
instance.export
|
||||
end
|
||||
end
|
||||
|
||||
context "exporting a user with a question which has been answered" do
|
||||
let!(:question) { FactoryBot.create(:question, user: user, author_is_anonymous: false) }
|
||||
let!(:answers) { FactoryBot.create_list(:answer, 5, question: question, user: FactoryBot.create(:user)) }
|
||||
|
||||
it "collects questions and answers" do
|
||||
subject
|
||||
expect(instance.instance_variable_get(:@obj)[:questions]).to eq([
|
||||
{
|
||||
answer_count: 5,
|
||||
answers: answers.map do |a|
|
||||
{
|
||||
comment_count: 0,
|
||||
comments: [],
|
||||
content: a.content,
|
||||
created_at: a.reload.created_at,
|
||||
id: a.id,
|
||||
smile_count: a.smile_count,
|
||||
user: instance.send(:user_stub, a.user)
|
||||
}
|
||||
end,
|
||||
author_is_anonymous: false,
|
||||
content: question.content,
|
||||
created_at: question.reload.created_at,
|
||||
id: question.id
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#collect_answers" do
|
||||
let!(:answers) { FactoryBot.create_list(:answer, 25, user: user) }
|
||||
|
||||
subject { instance.send(:collect_answers) }
|
||||
|
||||
it "collects answers" do
|
||||
subject
|
||||
expect(instance.instance_variable_get(:@obj)[:answers]).to eq(answers.map do |a|
|
||||
{
|
||||
comment_count: 0,
|
||||
comments: [],
|
||||
content: a.content,
|
||||
created_at: a.reload.created_at,
|
||||
id: a.id,
|
||||
question: instance.send(:process_question,
|
||||
a.question.reload,
|
||||
include_user: true,
|
||||
include_answers: false),
|
||||
smile_count: 0
|
||||
}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#collect_comments" do
|
||||
let!(:comments) do
|
||||
FactoryBot.create_list(:comment,
|
||||
25,
|
||||
user: user,
|
||||
answer: FactoryBot.create(:answer, user: FactoryBot.create(:user)))
|
||||
end
|
||||
|
||||
subject { instance.send(:collect_comments) }
|
||||
|
||||
it "collects comments" do
|
||||
subject
|
||||
expect(instance.instance_variable_get(:@obj)[:comments]).to eq(comments.map do |c|
|
||||
{
|
||||
content: c.content,
|
||||
created_at: c.reload.created_at,
|
||||
id: c.id,
|
||||
answer: instance.send(:process_answer,
|
||||
c.answer,
|
||||
include_comments: false)
|
||||
}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#collect_smiles" do
|
||||
let!(:smiles) { FactoryBot.create_list(:smile, 25, user: user) }
|
||||
|
||||
subject { instance.send(:collect_smiles) }
|
||||
|
||||
it "collects reactions" do
|
||||
subject
|
||||
expect(instance.instance_variable_get(:@obj)[:smiles]).to eq(smiles.map do |s|
|
||||
{
|
||||
id: s.id,
|
||||
created_at: s.reload.created_at,
|
||||
answer: {
|
||||
comment_count: s.parent.comment_count,
|
||||
content: s.parent.content,
|
||||
created_at: s.parent.reload.created_at,
|
||||
id: s.parent.id,
|
||||
question: {
|
||||
answer_count: s.parent.question.answer_count,
|
||||
author_is_anonymous: s.parent.question.author_is_anonymous,
|
||||
content: s.parent.question.content,
|
||||
created_at: s.parent.question.reload.created_at,
|
||||
id: s.parent.question.id,
|
||||
user: nil # we're not populating this in the factory
|
||||
},
|
||||
smile_count: s.parent.smile_count
|
||||
}
|
||||
}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#finalize" do
|
||||
let(:fake_rails_root) { Pathname(Dir.mktmpdir) }
|
||||
let(:dir) { instance.instance_variable_get(:@export_dirname) }
|
||||
let(:name) { instance.instance_variable_get(:@export_filename) }
|
||||
|
||||
before do
|
||||
instance.instance_variable_set(:@obj, {
|
||||
some: {
|
||||
sample: {
|
||||
data: "Text"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Dir.mkdir("#{fake_rails_root}/public")
|
||||
FileUtils.cp_r(Rails.root.join("public/images"), "#{fake_rails_root}/public/images")
|
||||
allow(Rails).to receive(:root).and_return(fake_rails_root)
|
||||
allow(UseCase::DataExport::Base)
|
||||
.to receive(:descendants)
|
||||
.and_return([ExampleExporter])
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_r(fake_rails_root)
|
||||
end
|
||||
it "creates a zip file with the expected contents" do
|
||||
subject
|
||||
|
||||
subject { instance.send(:finalize) }
|
||||
# check created zip file
|
||||
zip_path = Rails.public_path.join("export/#{export_name}.zip")
|
||||
expect(File.exist?(zip_path)).to be true
|
||||
|
||||
context "exporting a user without a profile picture or header" do
|
||||
it "prepares files to be archived" do
|
||||
subject
|
||||
expect(File.directory?(fake_rails_root.join("public/export"))).to eq(true)
|
||||
expect(File.directory?("#{dir}/pictures")).to eq(true)
|
||||
end
|
||||
Zip::File.open(zip_path) do |zip|
|
||||
# check for zip comment
|
||||
expect(zip.comment).to eq "justask export done for fizzyraccoon on 2022-12-10T13:37:42Z\n"
|
||||
|
||||
it "outputs JSON" do
|
||||
subject
|
||||
path = "#{dir}/#{name}.json"
|
||||
expect(File.exist?(path)).to eq(true)
|
||||
expect(JSON.load_file(path, symbolize_names: true)).to eq(instance.instance_variable_get(:@obj))
|
||||
end
|
||||
# check if all files and directories are there
|
||||
expect(zip.entries.map(&:name).sort).to eq([
|
||||
# basic dirs from exporter
|
||||
"#{export_name}/",
|
||||
"#{export_name}/pictures/",
|
||||
# files added by the ExampleExporter
|
||||
"#{export_name}/textfile.txt",
|
||||
"#{export_name}/pictures/example.jpg",
|
||||
"#{export_name}/some.json"
|
||||
].sort)
|
||||
|
||||
it "outputs YAML" do
|
||||
subject
|
||||
path = "#{dir}/#{name}.yml"
|
||||
expect(File.exist?(path)).to eq(true)
|
||||
expect(YAML.load_file(path)).to eq(instance.instance_variable_get(:@obj))
|
||||
end
|
||||
|
||||
it "outputs XML" do
|
||||
subject
|
||||
path = "#{dir}/#{name}.xml"
|
||||
expect(File.exist?(path)).to eq(true)
|
||||
# check if the file contents match
|
||||
expect(zip.file.read("#{export_name}/textfile.txt")).to eq("Sample Text\n")
|
||||
expect(Base64.encode64(zip.file.read("#{export_name}/pictures/example.jpg")))
|
||||
.to eq(Base64.encode64(File.read(File.expand_path("../fixtures/files/banana_racc.jpg", __dir__))))
|
||||
expect(zip.file.read("#{export_name}/some.json")).to eq(<<~JSON)
|
||||
{
|
||||
"animals": [
|
||||
"raccoon",
|
||||
"fox",
|
||||
"hyena",
|
||||
"deer",
|
||||
"dog"
|
||||
],
|
||||
"big_number": 3457812374589235798,
|
||||
"booleans": {
|
||||
"yes": true,
|
||||
"no": false,
|
||||
"file_not_found": null
|
||||
}
|
||||
}
|
||||
JSON
|
||||
end
|
||||
end
|
||||
|
||||
context "exporting a user with a profile header" do
|
||||
it "updates the export fields of the user" do
|
||||
expect { subject }.to change { user.export_processing }.from(true).to(false)
|
||||
expect(user.export_url).to eq("https://example.com/export/#{export_name}.zip")
|
||||
expect(user.export_created_at).to eq(Time.utc(2022, 12, 10, 13, 37, 42))
|
||||
expect(user).to be_persisted
|
||||
end
|
||||
|
||||
context "when exporting fails" do
|
||||
let(:zipfile_deletion_expected) { true }
|
||||
|
||||
before do
|
||||
user.profile_header = Rack::Test::UploadedFile.new(File.open("#{file_fixture_path}/banana_racc.jpg"))
|
||||
user.save!
|
||||
allow_any_instance_of(ExampleExporter).to receive(:files).and_raise(ArgumentError.new("just testing"))
|
||||
end
|
||||
|
||||
it "exports the header image" do
|
||||
subject
|
||||
dirname = instance.instance_variable_get(:@export_dirname)
|
||||
%i[web mobile retina original].each do |size|
|
||||
expect(File.exist?("#{dirname}/pictures/header_#{size}_banana_racc.jpg")).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
it "deletes the zip file" do
|
||||
expect { subject }.to raise_error(ArgumentError, "just testing")
|
||||
|
||||
context "exporting a user with a profile picture" do
|
||||
before do
|
||||
user.profile_picture = Rack::Test::UploadedFile.new(File.open("#{file_fixture_path}/banana_racc.jpg"))
|
||||
user.save!
|
||||
end
|
||||
|
||||
it "exports the header image" do
|
||||
subject
|
||||
dirname = instance.instance_variable_get(:@export_dirname)
|
||||
%i[large medium small original].each do |size|
|
||||
expect(File.exist?("#{dirname}/pictures/picture_#{size}_banana_racc.jpg")).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#publish" do
|
||||
let(:fake_rails_root) { Pathname(Dir.mktmpdir) }
|
||||
let(:fake_rails_public_path) { fake_rails_root.join('public') }
|
||||
let(:name) { instance.instance_variable_get(:@export_filename) }
|
||||
|
||||
before do
|
||||
FileUtils.mkdir_p("#{fake_rails_root}/public/export")
|
||||
allow(Rails).to receive(:root).and_return(fake_rails_root)
|
||||
allow(Rails).to receive(:public_path).and_return(fake_rails_public_path)
|
||||
|
||||
user.export_processing = true
|
||||
user.save!
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_r(fake_rails_root)
|
||||
end
|
||||
|
||||
subject { instance.send(:publish) }
|
||||
|
||||
it "publishes an archive" do
|
||||
freeze_time do
|
||||
expect { subject }.to change { user.export_processing }.from(true).to(false)
|
||||
expect(File.exist?("#{fake_rails_root}/public/export/#{name}.tar.gz")).to eq(true)
|
||||
expect(user.export_url).to eq("https://example.com/export/#{name}.tar.gz")
|
||||
expect(user.export_created_at).to eq(Time.now.utc)
|
||||
expect(user).to be_persisted
|
||||
zip_path = Rails.public_path.join("export/#{export_name}.zip")
|
||||
expect(File.exist?(zip_path)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/answers"
|
||||
|
||||
describe UseCase::DataExport::Answers, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have any answers" do
|
||||
it "returns an empty set of answers" do
|
||||
expect(json_file("answers.json")).to eq(
|
||||
{
|
||||
answers: []
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has made some answer" do
|
||||
let!(:answer) do
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) { FactoryBot.create(:answer, user:, content: "Yay, data export!") }
|
||||
end
|
||||
|
||||
it "returns the answers as json" do
|
||||
expect(json_file("answers.json")).to eq(
|
||||
{
|
||||
answers: [
|
||||
{
|
||||
id: answer.id,
|
||||
content: "Yay, data export!",
|
||||
question_id: answer.question.id,
|
||||
comment_count: 0,
|
||||
user_id: user.id,
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z",
|
||||
smile_count: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/appendables"
|
||||
|
||||
describe UseCase::DataExport::Appendables, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have any appendable" do
|
||||
it "returns an empty set of appendables" do
|
||||
expect(json_file("appendables.json")).to eq(
|
||||
{
|
||||
appendables: []
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has smiled some things" do
|
||||
let(:answer) { FactoryBot.create(:answer, user:) }
|
||||
let(:comment) { FactoryBot.create(:comment, user:, answer:) }
|
||||
|
||||
let!(:appendables) do
|
||||
[
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) { FactoryBot.create(:comment_smile, user:, parent: comment) },
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 39, 21)) { FactoryBot.create(:smile, user:, parent: answer) }
|
||||
]
|
||||
end
|
||||
|
||||
it "returns the appendables as json" do
|
||||
expect(json_file("appendables.json")).to eq(
|
||||
{
|
||||
appendables: [
|
||||
{
|
||||
id: appendables[0].id,
|
||||
type: "Appendable::Reaction",
|
||||
user_id: user.id,
|
||||
parent_id: appendables[0].parent_id,
|
||||
parent_type: "Comment",
|
||||
content: "🙂",
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z"
|
||||
},
|
||||
{
|
||||
id: appendables[1].id,
|
||||
type: "Appendable::Reaction",
|
||||
user_id: user.id,
|
||||
parent_id: appendables[1].parent_id,
|
||||
parent_type: "Answer",
|
||||
content: "🙂",
|
||||
created_at: "2022-12-10T13:39:21.000Z",
|
||||
updated_at: "2022-12-10T13:39:21.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/comments"
|
||||
|
||||
describe UseCase::DataExport::Comments, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have any comments" do
|
||||
it "returns an empty set of comments" do
|
||||
expect(json_file("comments.json")).to eq(
|
||||
{
|
||||
comments: []
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has made some comment" do
|
||||
let(:answer) { FactoryBot.create(:answer, user:) }
|
||||
|
||||
let!(:comment) do
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) { FactoryBot.create(:comment, user:, answer:, content: "Yay, data export!") }
|
||||
end
|
||||
|
||||
it "returns the comments as json" do
|
||||
expect(json_file("comments.json")).to eq(
|
||||
{
|
||||
comments: [
|
||||
{
|
||||
id: comment.id,
|
||||
content: "Yay, data export!",
|
||||
answer_id: answer.id,
|
||||
user_id: user.id,
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z",
|
||||
smile_count: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/inbox_entries"
|
||||
|
||||
describe UseCase::DataExport::InboxEntries, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have anything in their inbox" do
|
||||
it "returns an empty set of inbox entries" do
|
||||
expect(json_file("inbox_entries.json")).to eq(
|
||||
{
|
||||
inbox_entries: []
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has some questions in their inbox" do
|
||||
let!(:inbox_entries) do
|
||||
[
|
||||
# using `Inbox.create` here as for some reason FactoryBot.create(:inbox) always sets `new` to `nil`???
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) { Inbox.create(user:, question: FactoryBot.create(:question), new: false) },
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 39, 21)) { Inbox.create(user:, question: FactoryBot.create(:question), new: true) }
|
||||
]
|
||||
end
|
||||
|
||||
it "returns the inbox entries as json" do
|
||||
expect(json_file("inbox_entries.json")).to eq(
|
||||
{
|
||||
inbox_entries: [
|
||||
{
|
||||
id: inbox_entries[0].id,
|
||||
user_id: user.id,
|
||||
question_id: inbox_entries[0].question_id,
|
||||
new: false,
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z"
|
||||
},
|
||||
{
|
||||
id: inbox_entries[1].id,
|
||||
user_id: user.id,
|
||||
question_id: inbox_entries[1].question_id,
|
||||
new: true,
|
||||
created_at: "2022-12-10T13:39:21.000Z",
|
||||
updated_at: "2022-12-10T13:39:21.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/mute_rules"
|
||||
|
||||
describe UseCase::DataExport::MuteRules, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have any mute rules" do
|
||||
it "returns an empty set of mute rules" do
|
||||
expect(json_file("mute_rules.json")).to eq(
|
||||
{
|
||||
mute_rules: []
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has some mute rules" do
|
||||
let!(:mute_rules) do
|
||||
[
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) { MuteRule.create(user:, muted_phrase: "test") },
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 39, 21)) { MuteRule.create(user:, muted_phrase: "python") }
|
||||
]
|
||||
end
|
||||
|
||||
it "returns the mute rules as json" do
|
||||
expect(json_file("mute_rules.json")).to eq(
|
||||
{
|
||||
mute_rules: [
|
||||
{
|
||||
id: mute_rules[0].id,
|
||||
user_id: user.id,
|
||||
muted_phrase: "test",
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z"
|
||||
},
|
||||
{
|
||||
id: mute_rules[1].id,
|
||||
user_id: user.id,
|
||||
muted_phrase: "python",
|
||||
created_at: "2022-12-10T13:39:21.000Z",
|
||||
updated_at: "2022-12-10T13:39:21.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,68 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/questions"
|
||||
|
||||
describe UseCase::DataExport::Questions, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have any questions" do
|
||||
it "returns an empty set of questions" do
|
||||
expect(json_file("questions.json")).to eq(
|
||||
{
|
||||
questions: []
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has made some questions" do
|
||||
let!(:questions) do
|
||||
[
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 12, 0)) { FactoryBot.create(:question, user:, content: "Yay, data export 1", author_is_anonymous: false, direct: false, answer_count: 12) },
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) { FactoryBot.create(:question, user:, content: "Yay, data export 2", author_is_anonymous: false, direct: true, answer_count: 1) },
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 39, 21)) { FactoryBot.create(:question, user:, content: "Yay, data export 3", author_is_anonymous: true, direct: true) }
|
||||
]
|
||||
end
|
||||
|
||||
it "returns the questions as json" do
|
||||
expect(json_file("questions.json")).to eq(
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
id: questions[0].id,
|
||||
content: "Yay, data export 1",
|
||||
author_is_anonymous: false,
|
||||
user_id: user.id,
|
||||
created_at: "2022-12-10T13:12:00.000Z",
|
||||
updated_at: "2022-12-10T13:12:00.000Z",
|
||||
answer_count: 12,
|
||||
direct: false
|
||||
},
|
||||
{
|
||||
id: questions[1].id,
|
||||
content: "Yay, data export 2",
|
||||
author_is_anonymous: false,
|
||||
user_id: user.id,
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z",
|
||||
answer_count: 1,
|
||||
direct: true
|
||||
},
|
||||
{
|
||||
id: questions[2].id,
|
||||
content: "Yay, data export 3",
|
||||
author_is_anonymous: true,
|
||||
user_id: user.id,
|
||||
created_at: "2022-12-10T13:39:21.000Z",
|
||||
updated_at: "2022-12-10T13:39:21.000Z",
|
||||
answer_count: 0,
|
||||
direct: true
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,64 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/relationships"
|
||||
|
||||
describe UseCase::DataExport::Relationships, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have any relationships" do
|
||||
it "returns an empty set of relationships" do
|
||||
expect(json_file("relationships.json")).to eq(
|
||||
{
|
||||
relationships: []
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has made some relationships" do
|
||||
let(:other_user) { FactoryBot.create(:user) }
|
||||
let(:blocked_user) { FactoryBot.create(:user) }
|
||||
let(:blocking_user) { FactoryBot.create(:user) }
|
||||
|
||||
let!(:relationships) do
|
||||
{
|
||||
# user <-> other_user follow each other
|
||||
user_to_other: travel_to(Time.utc(2022, 12, 10, 13, 12, 0)) { user.follow(other_user) },
|
||||
other_to_user: travel_to(Time.utc(2022, 12, 10, 13, 12, 36)) { other_user.follow(user) },
|
||||
|
||||
# user blocked blocked_user
|
||||
block: travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) { user.block(blocked_user) },
|
||||
|
||||
# user is blocked by blocking_user
|
||||
blocked_by: travel_to(Time.utc(2022, 12, 10, 13, 39, 21)) { blocking_user.block(user) }
|
||||
}
|
||||
end
|
||||
|
||||
it "returns the relationships as json" do
|
||||
expect(json_file("relationships.json")).to eq(
|
||||
{
|
||||
relationships: [
|
||||
{
|
||||
id: relationships[:user_to_other].id,
|
||||
source_id: user.id,
|
||||
target_id: other_user.id,
|
||||
created_at: "2022-12-10T13:12:00.000Z",
|
||||
updated_at: "2022-12-10T13:12:00.000Z",
|
||||
type: "Relationships::Follow"
|
||||
},
|
||||
{
|
||||
id: relationships[:block].id,
|
||||
source_id: user.id,
|
||||
target_id: blocked_user.id,
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z",
|
||||
type: "Relationships::Block"
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,58 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/theme"
|
||||
|
||||
describe UseCase::DataExport::Theme, :data_export do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
context "when user doesn't have a theme" do
|
||||
it "returns nothing" do
|
||||
expect(subject).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has a theme" do
|
||||
let!(:theme) do
|
||||
travel_to(Time.utc(2022, 12, 10, 13, 37, 42)) do
|
||||
FactoryBot.create(:theme, user:)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the theme as json" do
|
||||
expect(json_file("theme.json")).to eq(
|
||||
{
|
||||
theme: {
|
||||
id: theme.id,
|
||||
user_id: user.id,
|
||||
primary_color: 9342168,
|
||||
primary_text: 16777215,
|
||||
danger_color: 14257035,
|
||||
danger_text: 16777215,
|
||||
success_color: 12573067,
|
||||
success_text: 16777215,
|
||||
warning_color: 14261899,
|
||||
warning_text: 16777215,
|
||||
info_color: 9165273,
|
||||
info_text: 16777215,
|
||||
dark_color: 6710886,
|
||||
dark_text: 15658734,
|
||||
raised_background: 16777215,
|
||||
background_color: 13026795,
|
||||
body_text: 3355443,
|
||||
muted_text: 3355443,
|
||||
created_at: "2022-12-10T13:37:42.000Z",
|
||||
updated_at: "2022-12-10T13:37:42.000Z",
|
||||
input_color: 15789556,
|
||||
input_text: 6710886,
|
||||
raised_accent: 16250871,
|
||||
light_color: 16316922,
|
||||
light_text: 0,
|
||||
input_placeholder: 7107965
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,176 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
require "use_case/data_export/user"
|
||||
|
||||
describe UseCase::DataExport::User, :data_export do
|
||||
let(:user_params) do
|
||||
{
|
||||
email: "fizzyraccoon@bsnss.biz",
|
||||
answered_count: 144,
|
||||
asked_count: 72,
|
||||
comment_smiled_count: 15,
|
||||
commented_count: 12,
|
||||
confirmation_sent_at: 2.weeks.ago.utc,
|
||||
confirmed_at: 2.weeks.ago.utc + 1.hour,
|
||||
created_at: 2.weeks.ago.utc,
|
||||
current_sign_in_at: 8.hours.ago.utc,
|
||||
current_sign_in_ip: "198.51.100.220",
|
||||
last_sign_in_at: 1.hour.ago,
|
||||
last_sign_in_ip: "192.0.2.14",
|
||||
locale: "en",
|
||||
privacy_allow_anonymous_questions: true,
|
||||
privacy_allow_public_timeline: false,
|
||||
privacy_allow_stranger_answers: false,
|
||||
privacy_show_in_search: true,
|
||||
screen_name: "fizzyraccoon",
|
||||
show_foreign_themes: true,
|
||||
sign_in_count: 10,
|
||||
smiled_count: 28,
|
||||
profile: {
|
||||
display_name: "Fizzy Raccoon",
|
||||
description: "A small raccoon",
|
||||
location: "Binland",
|
||||
motivation_header: "",
|
||||
website: "https://retrospring.net"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it "returns the user as json" do
|
||||
expect(json_file("user.json")).to eq(
|
||||
{
|
||||
user: {
|
||||
id: user.id,
|
||||
email: "fizzyraccoon@bsnss.biz",
|
||||
remember_created_at: nil,
|
||||
sign_in_count: 10,
|
||||
current_sign_in_at: user.current_sign_in_at.as_json,
|
||||
last_sign_in_at: user.last_sign_in_at.as_json,
|
||||
current_sign_in_ip: "198.51.100.220",
|
||||
last_sign_in_ip: "192.0.2.14",
|
||||
created_at: user.created_at.as_json,
|
||||
updated_at: user.updated_at.as_json,
|
||||
screen_name: "fizzyraccoon",
|
||||
asked_count: 72,
|
||||
answered_count: 144,
|
||||
commented_count: 12,
|
||||
smiled_count: 28,
|
||||
profile_picture_file_name: nil,
|
||||
profile_picture_processing: nil,
|
||||
profile_picture_x: nil,
|
||||
profile_picture_y: nil,
|
||||
profile_picture_w: nil,
|
||||
profile_picture_h: nil,
|
||||
privacy_allow_anonymous_questions: true,
|
||||
privacy_allow_public_timeline: false,
|
||||
privacy_allow_stranger_answers: false,
|
||||
privacy_show_in_search: true,
|
||||
comment_smiled_count: 15,
|
||||
profile_header_file_name: nil,
|
||||
profile_header_processing: nil,
|
||||
profile_header_x: nil,
|
||||
profile_header_y: nil,
|
||||
profile_header_w: nil,
|
||||
profile_header_h: nil,
|
||||
locale: "en",
|
||||
confirmed_at: user.confirmed_at.as_json,
|
||||
confirmation_sent_at: user.confirmation_sent_at.as_json,
|
||||
unconfirmed_email: nil,
|
||||
show_foreign_themes: true,
|
||||
export_url: nil,
|
||||
export_processing: false,
|
||||
export_created_at: nil,
|
||||
otp_module: "disabled",
|
||||
privacy_lock_inbox: false,
|
||||
privacy_require_user: false,
|
||||
privacy_hide_social_graph: false,
|
||||
privacy_noindex: false
|
||||
},
|
||||
profile: {
|
||||
display_name: "Fizzy Raccoon",
|
||||
description: "A small raccoon",
|
||||
location: "Binland",
|
||||
website: "https://retrospring.net",
|
||||
motivation_header: "",
|
||||
created_at: user.profile.created_at.as_json,
|
||||
updated_at: user.profile.updated_at.as_json,
|
||||
anon_display_name: nil
|
||||
},
|
||||
roles: {
|
||||
administrator: false,
|
||||
moderator: false
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it "does not have any pictures attached" do
|
||||
expect(subject.keys.select { _1.start_with?("pictures/") }).to be_empty
|
||||
end
|
||||
|
||||
context "when user has a profile picture" do
|
||||
let(:user_params) do
|
||||
super().merge(
|
||||
process_profile_picture_upload: true, # force carrierwave_backgrounder to immediately process the image
|
||||
profile_picture: Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/files/banana_racc.jpg"), "image/jpeg"),
|
||||
profile_picture_x: 571,
|
||||
profile_picture_y: 353,
|
||||
profile_picture_w: 474,
|
||||
profile_picture_h: 474
|
||||
)
|
||||
end
|
||||
|
||||
it "includes the pictures in the file list" do
|
||||
expect(subject.keys.select { _1.start_with?("pictures/") }.sort).to eq([
|
||||
"pictures/profile_picture_original_banana_racc.jpg",
|
||||
"pictures/profile_picture_large_banana_racc.jpg",
|
||||
"pictures/profile_picture_medium_banana_racc.jpg",
|
||||
"pictures/profile_picture_small_banana_racc.jpg"
|
||||
].sort)
|
||||
end
|
||||
|
||||
it "contains the profile picture info on the exported user" do
|
||||
expect(json_file("user.json").fetch(:user)).to include(
|
||||
profile_picture_file_name: "banana_racc.jpg",
|
||||
profile_picture_x: 571,
|
||||
profile_picture_y: 353,
|
||||
profile_picture_w: 474,
|
||||
profile_picture_h: 474
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user has a profile header" do
|
||||
let(:user_params) do
|
||||
super().merge(
|
||||
process_profile_header_upload: true, # force carrierwave_backgrounder to immediately process the image
|
||||
profile_header: Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/files/banana_racc.jpg"), "image/jpeg"),
|
||||
profile_header_x: 0,
|
||||
profile_header_y: 412,
|
||||
profile_header_w: 1813,
|
||||
profile_header_h: 423
|
||||
)
|
||||
end
|
||||
|
||||
it "includes the pictures in the file list" do
|
||||
expect(subject.keys.select { _1.start_with?("pictures/") }.sort).to eq([
|
||||
"pictures/profile_header_original_banana_racc.jpg",
|
||||
"pictures/profile_header_web_banana_racc.jpg",
|
||||
"pictures/profile_header_mobile_banana_racc.jpg",
|
||||
"pictures/profile_header_retina_banana_racc.jpg"
|
||||
].sort)
|
||||
end
|
||||
|
||||
it "contains the profile header info on the exported user" do
|
||||
expect(json_file("user.json").fetch(:user)).to include(
|
||||
profile_header_file_name: "banana_racc.jpg",
|
||||
profile_header_x: 0,
|
||||
profile_header_y: 412,
|
||||
profile_header_w: 1813,
|
||||
profile_header_h: 423
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "json"
|
||||
|
||||
RSpec.shared_context "DataExport" do
|
||||
let(:user_params) { {} }
|
||||
|
||||
let!(:user) { FactoryBot.create(:user, **user_params) }
|
||||
|
||||
subject { described_class.call(user:) }
|
||||
|
||||
def json_file(filename) = JSON.parse(subject[filename], symbolize_names: true)
|
||||
end
|
||||
|
||||
RSpec.configure do |c|
|
||||
c.include_context "DataExport", data_export: true
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
raise ArgumentError.new("This file should only be required in the 'test' environment! Current environment: #{Rails.env}") unless Rails.env.test?
|
||||
|
||||
require "use_case/data_export/base"
|
||||
|
||||
# an example exporter to be used for the tests of `Exporter`
|
||||
#
|
||||
# this only returning basic files, nothing user-specific. each exporter should be tested individually.
|
||||
class ExampleExporter < UseCase::DataExport::Base
|
||||
def files = {
|
||||
"textfile.txt" => "Sample Text\n",
|
||||
"pictures/example.jpg" => File.read(File.expand_path("../fixtures/files/banana_racc.jpg", __dir__)),
|
||||
"some.json" => json_file!(
|
||||
animals: %w[raccoon fox hyena deer dog],
|
||||
big_number: 3457812374589235798,
|
||||
booleans: {
|
||||
yes: true,
|
||||
no: false,
|
||||
file_not_found: nil
|
||||
}
|
||||
)
|
||||
}
|
||||
end
|
Loading…
Reference in New Issue