invoices: fix 500 on the admin invoice page
All checks were successful
Build and Deploy to Production / test (push) Successful in 26s
Build and Deploy to Production / build (push) Successful in 1m2s
Build and Deploy to Production / deploy (push) Successful in 36s

Replacing the paid button with the cancel/hide action block cut the old
markup at the first <% end %>, which belonged to the button's inner
if/else, leaving a stray </button></form><% end %> behind. show.erb no
longer compiled and every admin invoice page returned 500; nothing
rendered that page in tests.

Remove the leftovers, render the page in every state (pending, paid,
cancelled, hidden), and compile every view in a test so an unbalanced
template fails CI even when no test visits that page.

Assisted-by: Claude:opus-5
This commit is contained in:
Sergei Poljanski 2026-10-10 08:47:23 +04:00
commit bf4f270fb4
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
3 changed files with 29 additions and 3 deletions

View file

@ -44,6 +44,22 @@ class InvoiceCancelTest < Minitest::Test
assert_equal 'pending', @inv.status
end
# Every state of the admin invoice page renders (pending, paid,
# cancelled, hidden), for a superuser and for a read-only user.
def test_admin_invoice_page_renders_in_every_state
admin_login!
get "/admin/invoices/#{@inv.uuid}"
assert last_response.ok?, last_response.body[0, 300]
assert_includes last_response.body, 'Cancel invoice'
@inv.update(paid_at: Time.now.utc)
get "/admin/invoices/#{@inv.uuid}"
assert last_response.ok?, last_response.body[0, 300]
@inv.update(cancelled_at: Time.now.utc, cancel_reason: 'dup', hidden: true)
get "/admin/invoices/#{@inv.uuid}"
assert last_response.ok?, last_response.body[0, 300]
assert_includes last_response.body, 'Reinstate'
end
def test_cancelled_pdf_renders
@inv.cancelled_at = Time.now.utc
@inv.cancel_reason = 'wrong client'

13
test/templates_test.rb Normal file
View file

@ -0,0 +1,13 @@
require_relative 'test_helper'
# A template with unbalanced <% end %> only fails when that page is
# rendered; compile every view up front so CI catches it regardless.
class TemplatesTest < Minitest::Test
Dir[File.join(__dir__, '..', 'views', '**', '*.erb')].each do |path|
name = path.sub(%r{.*/views/}, '').tr('/.', '__')
define_method("test_compiles_#{name}") do
tmpl = Tilt::ErubiTemplate.new(path, escape_html: true)
tmpl.send(:compiled_method, [], Object) # raises SyntaxError if broken
end
end
end

View file

@ -68,9 +68,6 @@
</form>
<% end %>
</div>
<% end %>
</button>
</form>
<% end %>
<p style="margin-top: 14px;">
<% if can?('invoices', 'write') %><a href="/admin/invoices/<%= @invoice.uuid %>/duplicate">Use as template for a new invoice →</a><% end %>