# frozen_string_literal: true require 'coveralls' Coveralls.wear!('rails') ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' require 'action_dispatch' require 'rails/test_help' require 'vcr' require 'sidekiq/testing' require 'minitest/reporters' include ActionDispatch::TestProcess VCR.configure do |config| config.cassette_library_dir = 'test/vcr_cassettes' config.hook_into :webmock config.filter_sensitive_data('sk_test_testfaketestfaketestfake') { Setting.get('stripe_secret_key') } config.filter_sensitive_data('pk_test_faketestfaketestfaketest') { Setting.get('stripe_public_key') } end Sidekiq::Testing.fake! Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true)] unless ENV['RM_INFO'] class ActiveSupport::TestCase # Add more helper methods to be used by all tests here... ActiveRecord::Migration.check_pending! fixtures :all def json_response(body) JSON.parse(body, symbolize_names: true) end def default_headers { 'Accept' => Mime[:json], 'Content-Type' => Mime[:json].to_s } end def open_api_headers(token) { 'Accept' => Mime[:json], 'Content-Type' => Mime[:json].to_s, 'Authorization' => "Token token=#{token}" } end def stripe_payment_method(error: nil) number = '4242424242424242' exp_month = 4 exp_year = DateTime.current.next_year.year cvc = '314' case error when /card_declined/ number = '4000000000000002' when /incorrect_number/ number = '4242424242424241' when /invalid_expiry_month/ exp_month = 15 when /invalid_expiry_year/ exp_year = 1964 when /invalid_cvc/ cvc = '99' when /require_3ds/ number = '4000002760003184' end Stripe::PaymentMethod.create( { type: 'card', card: { number: number, exp_month: exp_month, exp_year: exp_year, cvc: cvc } }, { api_key: Setting.get('stripe_secret_key') } ).id end # Force the invoice generation worker to run NOW and check the resulting file generated. # Delete the file afterwards. # @param invoice {Invoice} def assert_invoice_pdf(invoice) assert_not_nil invoice, 'Invoice was not created' invoice_worker = InvoiceWorker.new invoice_worker.perform(invoice.id, invoice&.user&.subscription&.expired_at) assert File.exist?(invoice.file), 'Invoice PDF was not generated' # now we check the file content reader = PDF::Reader.new(invoice.file) assert_equal 1, reader.page_count # single page invoice ht_amount = invoice.total page = reader.pages.first lines = page.text.scan(/^.+/) lines.each do |line| # check that the numbers printed into the PDF file match the total stored in DB if line.include? I18n.t('invoices.total_amount') assert_equal invoice.total / 100.0, parse_amount_from_invoice_line(line), 'Invoice total rendered in the PDF file does not match' end # check that the VAT was correctly applied if it was configured ht_amount = parse_amount_from_invoice_line(line) if line.include? I18n.t('invoices.including_total_excluding_taxes') end vat_service = VatHistoryService.new invoice.invoice_items.each do |item| vat_rate = vat_service.invoice_vat(item) if vat_rate.positive? computed_ht = sprintf('%.2f', (item.amount_after_coupon / (vat_rate / 100.00 + 1)) / 100.00).to_f assert_equal computed_ht, item.net_amount / 100.00, 'Total excluding taxes rendered in the PDF file is not computed correctly' else assert_equal item.amount_after_coupon, item.net_amount, 'VAT information was rendered in the PDF file despite that VAT was disabled' end end # check the recipient & the address if invoice.invoicing_profile.organization assert lines.first.include?(invoice.invoicing_profile.organization.name), 'On the PDF invoice, organization name is invalid' assert invoice.invoicing_profile.organization.address.address.include?(lines[2].split(' ').last.strip), 'On the PDF invoice, organization address is invalid' else assert lines.first.include?(invoice.invoicing_profile.full_name), 'On the PDF invoice, customer name is invalid' assert invoice.invoicing_profile.address.address.include?(lines[2].split(' ').last.strip), 'On the PDF invoice, customer address is invalid' end # check the email assert lines[1].include?(invoice.invoicing_profile.email), 'On the PDF invoice, email is invalid' File.delete(invoice.file) end # Force the statistics export generation worker to run NOW and check the resulting file generated. # Delete the file afterwards. # @param export {Export} def assert_export_xlsx(export) assert_not_nil export, 'Export was not created' if export.category == 'statistics' export_worker = StatisticsExportWorker.new export_worker.perform(export.id) assert File.exist?(export.file), 'Export XLSX was not generated' File.delete(export.file) else skip('Unable to test export which is not of the category "statistics"') end end def assert_archive(accounting_period) assert_not_nil accounting_period, 'AccountingPeriod was not created' archive_worker = ArchiveWorker.new archive_worker.perform(accounting_period.id) assert FileTest.exist?(accounting_period.archive_file), 'ZIP archive was not generated' # Extract archive require 'tmpdir' require 'fileutils' dest = "#{Dir.tmpdir}/accounting/#{accounting_period.id}" FileUtils.mkdir_p "#{dest}/accounting" Zip::File.open(accounting_period.archive_file) do |zip_file| # Handle entries one by one zip_file.each do |entry| # Extract to file/directory/symlink entry.extract("#{dest}/#{entry.name}") end end # Check archive matches require 'integrity/checksum' sumfile = File.read("#{dest}/checksum.sha256").split("\t") assert_equal sumfile[0], Integrity::Checksum.file("#{dest}/#{sumfile[1]}"), 'archive checksum does not match' archive = File.read("#{dest}/#{sumfile[1]}") archive_json = JSON.parse(archive) invoices = Invoice.where( 'created_at >= :start_date AND created_at <= :end_date', start_date: accounting_period.start_at.to_datetime, end_date: accounting_period.end_at.to_datetime ) assert_equal invoices.count, archive_json['invoices'].count assert_equal accounting_period.footprint, archive_json['period_footprint'] require 'version' assert_equal Version.current, archive_json['software']['version'] # we clean up the files before quitting FileUtils.rm_rf(dest) FileUtils.rm_rf(accounting_period.archive_folder) end def assert_dates_equal(expected, actual, msg = nil) assert_not_nil actual, msg assert_equal expected.to_date, actual.to_date, msg end private # Parse a line of text read from a PDF file and return the price included inside # Line of text should be of form 'Label $10.00' # @returns {float} def parse_amount_from_invoice_line(line) line[line.rindex(' ') + 1..-1].tr(I18n.t('number.currency.format.unit'), '').to_f end end class ActionDispatch::IntegrationTest include Warden::Test::Helpers Warden.test_mode! end