mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-06 05:55:21 +02:00
Data exports (#2517)
* Import / export UI * Data exports * Lint fixes, brakeman update
This commit is contained in:
parent
b7c56e2fb7
commit
0329a5f211
20 changed files with 717 additions and 2 deletions
73
test/controllers/family_exports_controller_test.rb
Normal file
73
test/controllers/family_exports_controller_test.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
require "test_helper"
|
||||
|
||||
class FamilyExportsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = users(:family_admin)
|
||||
@non_admin = users(:family_member)
|
||||
@family = @admin.family
|
||||
|
||||
sign_in @admin
|
||||
end
|
||||
|
||||
test "non-admin cannot access exports" do
|
||||
sign_in @non_admin
|
||||
|
||||
get new_family_export_path
|
||||
assert_redirected_to root_path
|
||||
|
||||
post family_exports_path
|
||||
assert_redirected_to root_path
|
||||
|
||||
get family_exports_path
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "admin can view export modal" do
|
||||
get new_family_export_path
|
||||
assert_response :success
|
||||
assert_select "h2", text: "Export your data"
|
||||
end
|
||||
|
||||
test "admin can create export" do
|
||||
assert_enqueued_with(job: FamilyDataExportJob) do
|
||||
post family_exports_path
|
||||
end
|
||||
|
||||
assert_redirected_to settings_profile_path
|
||||
assert_equal "Export started. You'll be able to download it shortly.", flash[:notice]
|
||||
|
||||
export = @family.family_exports.last
|
||||
assert_equal "pending", export.status
|
||||
end
|
||||
|
||||
test "admin can view export list" do
|
||||
export1 = @family.family_exports.create!(status: "completed")
|
||||
export2 = @family.family_exports.create!(status: "processing")
|
||||
|
||||
get family_exports_path
|
||||
assert_response :success
|
||||
|
||||
assert_match export1.filename, response.body
|
||||
assert_match "Exporting...", response.body
|
||||
end
|
||||
|
||||
test "admin can download completed export" do
|
||||
export = @family.family_exports.create!(status: "completed")
|
||||
export.export_file.attach(
|
||||
io: StringIO.new("test zip content"),
|
||||
filename: "test.zip",
|
||||
content_type: "application/zip"
|
||||
)
|
||||
|
||||
get download_family_export_path(export)
|
||||
assert_redirected_to(/rails\/active_storage/)
|
||||
end
|
||||
|
||||
test "cannot download incomplete export" do
|
||||
export = @family.family_exports.create!(status: "processing")
|
||||
|
||||
get download_family_export_path(export)
|
||||
assert_redirected_to settings_profile_path
|
||||
assert_equal "Export not ready for download", flash[:alert]
|
||||
end
|
||||
end
|
3
test/fixtures/family_exports.yml
vendored
Normal file
3
test/fixtures/family_exports.yml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
# Empty file - no fixtures needed, tests create them dynamically
|
32
test/jobs/family_data_export_job_test.rb
Normal file
32
test/jobs/family_data_export_job_test.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
require "test_helper"
|
||||
|
||||
class FamilyDataExportJobTest < ActiveJob::TestCase
|
||||
setup do
|
||||
@family = families(:dylan_family)
|
||||
@export = @family.family_exports.create!
|
||||
end
|
||||
|
||||
test "marks export as processing then completed" do
|
||||
assert_equal "pending", @export.status
|
||||
|
||||
perform_enqueued_jobs do
|
||||
FamilyDataExportJob.perform_later(@export)
|
||||
end
|
||||
|
||||
@export.reload
|
||||
assert_equal "completed", @export.status
|
||||
assert @export.export_file.attached?
|
||||
end
|
||||
|
||||
test "marks export as failed on error" do
|
||||
# Mock the exporter to raise an error
|
||||
Family::DataExporter.any_instance.stubs(:generate_export).raises(StandardError, "Export failed")
|
||||
|
||||
perform_enqueued_jobs do
|
||||
FamilyDataExportJob.perform_later(@export)
|
||||
end
|
||||
|
||||
@export.reload
|
||||
assert_equal "failed", @export.status
|
||||
end
|
||||
end
|
115
test/models/family/data_exporter_test.rb
Normal file
115
test/models/family/data_exporter_test.rb
Normal file
|
@ -0,0 +1,115 @@
|
|||
require "test_helper"
|
||||
|
||||
class Family::DataExporterTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@family = families(:dylan_family)
|
||||
@other_family = families(:empty)
|
||||
@exporter = Family::DataExporter.new(@family)
|
||||
|
||||
# Create some test data for the family
|
||||
@account = @family.accounts.create!(
|
||||
name: "Test Account",
|
||||
accountable: Depository.new,
|
||||
balance: 1000,
|
||||
currency: "USD"
|
||||
)
|
||||
|
||||
@category = @family.categories.create!(
|
||||
name: "Test Category",
|
||||
color: "#FF0000"
|
||||
)
|
||||
|
||||
@tag = @family.tags.create!(
|
||||
name: "Test Tag",
|
||||
color: "#00FF00"
|
||||
)
|
||||
end
|
||||
|
||||
test "generates a zip file with all required files" do
|
||||
zip_data = @exporter.generate_export
|
||||
|
||||
assert zip_data.is_a?(StringIO)
|
||||
|
||||
# Check that the zip contains all expected files
|
||||
expected_files = [ "accounts.csv", "transactions.csv", "trades.csv", "categories.csv", "all.ndjson" ]
|
||||
|
||||
Zip::File.open_buffer(zip_data) do |zip|
|
||||
actual_files = zip.entries.map(&:name)
|
||||
assert_equal expected_files.sort, actual_files.sort
|
||||
end
|
||||
end
|
||||
|
||||
test "generates valid CSV files" do
|
||||
zip_data = @exporter.generate_export
|
||||
|
||||
Zip::File.open_buffer(zip_data) do |zip|
|
||||
# Check accounts.csv
|
||||
accounts_csv = zip.read("accounts.csv")
|
||||
assert accounts_csv.include?("id,name,type,subtype,balance,currency,created_at")
|
||||
|
||||
# Check transactions.csv
|
||||
transactions_csv = zip.read("transactions.csv")
|
||||
assert transactions_csv.include?("date,account_name,amount,name,category,tags,notes,currency")
|
||||
|
||||
# Check trades.csv
|
||||
trades_csv = zip.read("trades.csv")
|
||||
assert trades_csv.include?("date,account_name,ticker,quantity,price,amount,currency")
|
||||
|
||||
# Check categories.csv
|
||||
categories_csv = zip.read("categories.csv")
|
||||
assert categories_csv.include?("name,color,parent_category,classification")
|
||||
end
|
||||
end
|
||||
|
||||
test "generates valid NDJSON file" do
|
||||
zip_data = @exporter.generate_export
|
||||
|
||||
Zip::File.open_buffer(zip_data) do |zip|
|
||||
ndjson_content = zip.read("all.ndjson")
|
||||
lines = ndjson_content.split("\n")
|
||||
|
||||
lines.each do |line|
|
||||
assert_nothing_raised { JSON.parse(line) }
|
||||
end
|
||||
|
||||
# Check that each line has expected structure
|
||||
first_line = JSON.parse(lines.first)
|
||||
assert first_line.key?("type")
|
||||
assert first_line.key?("data")
|
||||
end
|
||||
end
|
||||
|
||||
test "only exports data from the specified family" do
|
||||
# Create data for another family that should NOT be exported
|
||||
other_account = @other_family.accounts.create!(
|
||||
name: "Other Family Account",
|
||||
accountable: Depository.new,
|
||||
balance: 5000,
|
||||
currency: "USD"
|
||||
)
|
||||
|
||||
other_category = @other_family.categories.create!(
|
||||
name: "Other Family Category",
|
||||
color: "#0000FF"
|
||||
)
|
||||
|
||||
zip_data = @exporter.generate_export
|
||||
|
||||
Zip::File.open_buffer(zip_data) do |zip|
|
||||
# Check accounts.csv doesn't contain other family's data
|
||||
accounts_csv = zip.read("accounts.csv")
|
||||
assert accounts_csv.include?(@account.name)
|
||||
refute accounts_csv.include?(other_account.name)
|
||||
|
||||
# Check categories.csv doesn't contain other family's data
|
||||
categories_csv = zip.read("categories.csv")
|
||||
assert categories_csv.include?(@category.name)
|
||||
refute categories_csv.include?(other_category.name)
|
||||
|
||||
# Check NDJSON doesn't contain other family's data
|
||||
ndjson_content = zip.read("all.ndjson")
|
||||
refute ndjson_content.include?(other_account.id)
|
||||
refute ndjson_content.include?(other_category.id)
|
||||
end
|
||||
end
|
||||
end
|
7
test/models/family_export_test.rb
Normal file
7
test/models/family_export_test.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require "test_helper"
|
||||
|
||||
class FamilyExportTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue