# frozen_string_literal: true require "rails_helper" require "exporter" 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(:instance) { described_class.new(user) } before do stub_const("APP_CONFIG", { "hostname" => "example.com", "https" => true, "items_per_page" => 5, "fog" => {} }) 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 })) end end describe "#collect_questions" do subject { instance.send(:collect_questions) } 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) 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) end after do FileUtils.rm_r(fake_rails_root) end subject { instance.send(:finalize) } 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 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 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) end end context "exporting a user with a profile header" do before do user.profile_header = 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[web mobile retina original].each do |size| expect(File.exist?("#{dirname}/pictures/header_#{size}_banana_racc.jpg")).to eq(true) end end end 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(: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) 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 end end end end