diff --git a/.gitignore b/.gitignore index f641c69..bb38e0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ Gemfile.lock *.gem +.rspec_status +/coverage/ /spec/dummy/log/ /spec/dummy/tmp/ +/spec/dummy/csv/ +/spec/dummy/db/development.sqlite3 +/spec/dummy/db/test.sqlite3 diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..34c5164 --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--format documentation +--color +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index ff25bf7..f424edc 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -30,3 +30,15 @@ Naming/FileName: Style/EvalWithLocation: Exclude: - lib/active_admin/async_exporter/reports/worker.rb + +Style/StringLiterals: + Exclude: + - spec/dummy/db/schema.rb + +Style/NumericLiterals: + Exclude: + - spec/dummy/db/schema.rb + +Layout/EmptyLinesAroundBlockBody: + Exclude: + - spec/dummy/db/schema.rb diff --git a/activeadmin-async_exporter.gemspec b/activeadmin-async_exporter.gemspec index d4020dd..a348748 100644 --- a/activeadmin-async_exporter.gemspec +++ b/activeadmin-async_exporter.gemspec @@ -23,10 +23,10 @@ Gem::Specification.new do |s| s.add_dependency 'rails', '>= 1.0.0' # Development dependencies + s.add_development_dependency 'pry-rails', '~> 0.3.9' s.add_development_dependency 'rspec', '~> 3.9.0' s.add_development_dependency 'rubocop-rails', '~> 2.3' s.add_development_dependency 'rubocop-rootstrap', '~> 0.1.0' s.add_development_dependency 'simplecov', '~> 0.17.1' s.add_development_dependency 'sqlite3', '1.4.1' - s.add_development_dependency 'pry-rails' end diff --git a/lib/active_admin/async_exporter/config.rb b/lib/active_admin/async_exporter/config.rb index 854a8df..d50ae0d 100644 --- a/lib/active_admin/async_exporter/config.rb +++ b/lib/active_admin/async_exporter/config.rb @@ -18,7 +18,7 @@ class Config def initialize @aws_bucket_name = nil @aws_bucket_folder_path = nil - @disk_folder_path = nil + @disk_folder_path = Rails.root.join('tmp/activeadmin-async_exporter') end end end diff --git a/lib/active_admin/async_exporter/reports/worker.rb b/lib/active_admin/async_exporter/reports/worker.rb index c60cae1..d2d82f1 100644 --- a/lib/active_admin/async_exporter/reports/worker.rb +++ b/lib/active_admin/async_exporter/reports/worker.rb @@ -15,7 +15,7 @@ def perform(options = {}) end file = Services::StorageService.call(path: path, name: file_name).store - AdminReport.find(options[:admin_report_id]).update_attributes!( + AdminReport.find(options[:admin_report_id]).update!( status: :ready, location_url: file.url ) diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb index 499e4fe..e9bb31e 100644 --- a/spec/dummy/config/application.rb +++ b/spec/dummy/config/application.rb @@ -10,6 +10,7 @@ require 'action_view/railtie' require 'sprockets/railtie' require 'active_storage/engine' +require 'pry' Bundler.require(*Rails.groups) diff --git a/spec/dummy/db/migrate/20200925172213_create_user.rb b/spec/dummy/db/migrate/20200925172213_create_user.rb new file mode 100644 index 0000000..ac162f2 --- /dev/null +++ b/spec/dummy/db/migrate/20200925172213_create_user.rb @@ -0,0 +1,7 @@ +class CreateUser < ActiveRecord::Migration[5.2] + def change + create_table :users do |t| + t.string :username, null: false + end + end +end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb new file mode 100644 index 0000000..b151eb5 --- /dev/null +++ b/spec/dummy/db/schema.rb @@ -0,0 +1,30 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2020_09_25_172213) do + + create_table "admin_reports", force: :cascade do |t| + t.integer "author_id" + t.string "entity", null: false + t.string "format", default: "csv", null: false + t.integer "status", null: false + t.string "location_url" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["author_id"], name: "index_admin_reports_on_author_id" + end + + create_table "users", force: :cascade do |t| + t.string "username", null: false + end + +end diff --git a/spec/fixtures/files/block.csv b/spec/fixtures/files/block.csv new file mode 100644 index 0000000..406399e --- /dev/null +++ b/spec/fixtures/files/block.csv @@ -0,0 +1,5 @@ +test_block +The username is: user_1 +The username is: user_2 +The username is: user_3 +The username is: AdminUser diff --git a/spec/fixtures/files/username.csv b/spec/fixtures/files/username.csv new file mode 100644 index 0000000..db64234 --- /dev/null +++ b/spec/fixtures/files/username.csv @@ -0,0 +1,5 @@ +username +user_1 +user_2 +user_3 +AdminUser diff --git a/spec/fixtures/files/username_and_block.csv b/spec/fixtures/files/username_and_block.csv new file mode 100644 index 0000000..e0b022b --- /dev/null +++ b/spec/fixtures/files/username_and_block.csv @@ -0,0 +1,5 @@ +username,test_block +user_1,The username is: user_1 +user_2,The username is: user_2 +user_3,The username is: user_3 +AdminUser,The username is: AdminUser diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 198ab85..71db309 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,16 @@ # frozen_string_literal: true -require 'bundler/setup' -require 'activeadmin-async_exporter' +ENV['RAILS_ENV'] ||= 'test' +require 'dummy/config/environment.rb' +require 'active_admin' +require 'support/time_helpers.rb' + +require 'simplecov' +SimpleCov.start + +Rails.application.routes.default_url_options[:host] = 'localhost:3000' + +require 'tmpdir' require 'simplecov' SimpleCov.start diff --git a/spec/support/time_helpers.rb b/spec/support/time_helpers.rb new file mode 100644 index 0000000..5fbf28e --- /dev/null +++ b/spec/support/time_helpers.rb @@ -0,0 +1,5 @@ +require 'active_support/testing/time_helpers' + +RSpec.configure do |config| + config.include ActiveSupport::Testing::TimeHelpers +end diff --git a/spec/worker_spec.rb b/spec/worker_spec.rb new file mode 100644 index 0000000..3ef6cb9 --- /dev/null +++ b/spec/worker_spec.rb @@ -0,0 +1,140 @@ +RSpec.describe ActiveAdmin::AsyncExporter::Worker do + let!(:users) do + %w[user_1 user_2 user_3].map do |username| + User.find_or_create_by!(username: username) + end + end + let(:controller) { Admin::UsersController.new } + let(:decorate_model) { false } + let(:file_name) { 'file_name' } + let(:query) { nil } + let(:csv_path) { Rails.root.join('tmp/activeadmin-async_exporter') } + let(:admin) { User.find_or_create_by!(username: 'AdminUser') } + let(:csv_fixture_file_path) { "./spec/fixtures/files/#{csv_fixture_file_name}.csv" } + let(:csv_fixture_file_name) { 'username' } + + let(:admin_report) do + AdminReport.create!(author_id: admin.id, + entity: User.name, + status: :pending) + end + + let(:columns) { { username: { block: false, value: 'username' } } } + + let(:options) do + { + admin_report_id: admin_report.id, + controller: controller.class.name, + columns: columns, + decorate_model: decorate_model, + file_name: file_name, + query: query + } + end + + subject do + described_class.perform_now(options) + end + + context 'with valid options' do + let(:test_date) { Time.current } + let(:full_file_name) { [file_name, test_date.to_i].join('_') + '.csv' } + let(:file_path) { "#{csv_path}/#{full_file_name}" } + + before do + travel_to(test_date) + end + + after do + FileUtils.rm(file_path) + travel_back + end + + shared_examples 'creates the physical file with data' do + it 'creates the physical file at the given location_url' do + expect { File.open(admin_report.location_url.to_s) }.to raise_exception(Errno::ENOENT) + subject + expect(File.open(admin_report.reload.location_url)).to be + end + + it 'physical path is the expected' do + subject + expect(admin_report.reload.location_url).to eq(file_path) + end + + it 'physical file contains data' do + subject + expect(File.open(admin_report.reload.location_url).read).to be + end + + it 'generated csv file contains the expected results' do + subject + + generated_file_data = File.open(admin_report.reload.location_url).read + + expect(File.open(csv_fixture_file_path).read).to eq(generated_file_data) + end + end + + it 'changes admin_report status to ready' do + expect { subject }.to change { admin_report.reload.status }.from('pending').to('ready') + end + + it 'changes admin_report location_url' do + expect { subject }.to change { admin_report.reload.location_url }.from(nil).to(file_path) + end + + context 'when the file_name is given' do + it 'full file name is the correct file name' do + subject + expect(admin_report.reload.location_url.split('/').last).to eq(full_file_name) + end + + it_behaves_like 'creates the physical file with data' + end + + context 'when the file_name is not given' do + let(:file_name) { nil } + let(:full_file_name) do + [controller.current_collection.name.pluralize.downcase, test_date.to_i].join('_') + '.csv' + end + + it 'full file name is the correct file name' do + subject + expect(admin_report.reload.location_url.split('/').last).to eq(full_file_name) + end + + it_behaves_like 'creates the physical file with data' + end + + context 'when the column is an attribute' do + it_behaves_like 'creates the physical file with data' + end + + context 'when the column is a block' do + let(:block) do + proc { |user| "The username is: #{user.username}" } + end + let(:csv_fixture_file_name) { 'block' } + let(:columns) { { test_block: { block: true, value: block.source } } } + + it_behaves_like 'creates the physical file with data' + end + + context 'when we have multiple columns' do + let(:block) do + proc { |user| "The username is: #{user.username}" } + end + + let(:csv_fixture_file_name) { 'username_and_block' } + let(:columns) do + { + username: { block: false, value: 'username' }, + test_block: { block: true, value: block.source } + } + end + + it_behaves_like 'creates the physical file with data' + end + end +end