pg_rails 7.0.8.pre.alpha.82 → 7.0.8.pre.alpha.84

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/pg_engine/app/admin/email_logs.rb +37 -0
  3. data/pg_engine/app/controllers/admin/email_logs_controller.rb +50 -0
  4. data/pg_engine/app/controllers/pg_engine/base_controller.rb +3 -0
  5. data/pg_engine/app/controllers/public/webhooks_controller.rb +77 -0
  6. data/pg_engine/app/decorators/email_log_decorator.rb +16 -0
  7. data/pg_engine/app/models/email.rb +16 -2
  8. data/pg_engine/app/models/email_log.rb +59 -0
  9. data/pg_engine/app/policies/email_log_policy.rb +31 -0
  10. data/pg_engine/app/views/admin/email_logs/_email_log.html.slim +1 -0
  11. data/pg_engine/app/views/admin/email_logs/_form.html.slim +16 -0
  12. data/pg_engine/app/views/admin/email_logs/show.html.slim +18 -0
  13. data/pg_engine/app/views/admin/emails/show.html.slim +20 -4
  14. data/pg_engine/app/views/pg_engine/base/index.html.slim +2 -0
  15. data/pg_engine/config/locales/es.yml +1 -0
  16. data/pg_engine/config/routes.rb +26 -0
  17. data/pg_engine/db/migrate/20240523183651_create_email_logs.rb +19 -0
  18. data/pg_engine/lib/pg_engine/email_observer.rb +4 -2
  19. data/pg_engine/lib/pg_engine/mailgun/log_sync.rb +36 -35
  20. data/pg_engine/lib/pg_engine/utils/check_invalid_records.rb +39 -0
  21. data/pg_engine/lib/pg_engine/utils/pg_logger.rb +20 -2
  22. data/pg_engine/spec/controllers/admin/email_logs_controller_spec.rb +179 -0
  23. data/pg_engine/spec/controllers/pg_engine/base_controller_spec.rb +11 -0
  24. data/pg_engine/spec/controllers/public/webhooks_controller_spec.rb +142 -0
  25. data/pg_engine/spec/factories/email_logs.rb +20 -0
  26. data/pg_engine/spec/lib/pg_engine/mailgun/log_sync_spec.rb +81 -7
  27. data/pg_engine/spec/models/email_log_spec.rb +48 -0
  28. data/pg_engine/spec/models/email_spec.rb +35 -0
  29. data/pg_layout/app/views/layouts/pg_layout/base.html.slim +11 -0
  30. data/pg_rails/lib/pg_rails/rspec_logger_matchers.rb +62 -0
  31. data/pg_rails/lib/version.rb +1 -1
  32. metadata +18 -2
@@ -5,7 +5,14 @@ require 'rainbow'
5
5
  # TODO: poder pasar blocks
6
6
 
7
7
  def pg_err(*args)
8
- raise args.first if ENV.fetch('RAISE_ERRORS', false) && args.first.is_a?(Exception)
8
+ if ENV.fetch('RAISE_ERRORS', false)
9
+ # :nocov:
10
+ raise args.first if args.first.is_a?(Exception)
11
+
12
+ raise StandardError, args
13
+
14
+ # :nocov:
15
+ end
9
16
 
10
17
  byebug if ENV.fetch('BYEBUG_ERRORS', false) # rubocop:disable Lint/Debugger
11
18
 
@@ -30,6 +37,10 @@ end
30
37
 
31
38
  module PgEngine
32
39
  class PgLogger
40
+ def self.test_logged_messages
41
+ @test_logged_messages ||= []
42
+ end
43
+
33
44
  class << self
34
45
  def log(type, *args)
35
46
  notify_all(build_msg(*args), type)
@@ -41,6 +52,7 @@ module PgEngine
41
52
  send_to_logger(mensaje, type)
42
53
  send_to_rollbar(mensaje, type)
43
54
  send_to_stdout(mensaje, type) if ENV.fetch('LOG_TO_STDOUT', nil)
55
+ save_internal(mensaje, type) if Rails.env.test?
44
56
  nil
45
57
  end
46
58
 
@@ -61,6 +73,10 @@ module PgEngine
61
73
  Rails.logger.send(type, rainbow_wrap(mensaje, type))
62
74
  end
63
75
 
76
+ def save_internal(mensaje, type)
77
+ PgEngine::PgLogger.test_logged_messages << [type, mensaje]
78
+ end
79
+
64
80
  # Format
65
81
 
66
82
  # TODO: loguear time
@@ -110,7 +126,9 @@ module PgEngine
110
126
 
111
127
  def cleaner
112
128
  bc = ActiveSupport::BacktraceCleaner.new
113
- bc.add_filter { |line| line.gsub(%r{.*pg_rails/}, '') }
129
+ bc.remove_silencers!
130
+ pattern = %r{\A[^/]+ \([\w.]+\) }
131
+ bc.add_silencer { |line| pattern.match?(line) && !line.match?(/pg_contable|pg_rails/) }
114
132
  bc.add_silencer { |line| /pg_logger/.match?(line) }
115
133
  bc
116
134
  end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generado con pg_rails
4
+
5
+ require 'rails_helper'
6
+
7
+ # This spec was generated by rspec-rails when you ran the scaffold generator.
8
+ # It demonstrates how one might use RSpec to specify the controller code that
9
+ # was generated by Rails when you ran the scaffold generator.
10
+ #
11
+ # It assumes that the implementation code is generated by the rails scaffold
12
+ # generator. If you are using any extension libraries to generate different
13
+ # controller code, this generated spec may or may not pass.
14
+ #
15
+ # It only uses APIs available in rails and/or rspec-rails. There are a number
16
+ # of tools you can use to make these specs even more expressive, but we're
17
+ # sticking to rails and rspec-rails APIs to keep things simple and stable.
18
+ #
19
+ # Compared to earlier versions of this generator, there is very limited use of
20
+ # stubs and message expectations in this spec. Stubs are only used when there
21
+ # is no simpler way to get a handle on the object needed for the example.
22
+ # Message expectations are only used when there is no simpler way to specify
23
+ # that an instance is receiving a specific message.
24
+ #
25
+ # Also compared to earlier versions of this generator, there are no longer any
26
+ # expectations of assigns and templates rendered. These features have been
27
+ # removed from Rails core in Rails 5, but can be added back in via the
28
+ # `rails-controller-testing` gem.
29
+
30
+ RSpec.describe Admin::EmailLogsController do
31
+ render_views
32
+ # This should return the minimal set of attributes required to create a valid
33
+ # EmailLog. As you add validations to EmailLog, be sure to
34
+ # adjust the attributes here as well.
35
+ let(:valid_attributes) do
36
+ attributes_for(:email_log)
37
+ end
38
+
39
+ let(:logged_user) { create :user, :developer }
40
+
41
+ before do
42
+ sign_in logged_user if logged_user.present?
43
+ end
44
+
45
+ describe '#mailgun_sync' do
46
+ subject do
47
+ post :mailgun_sync
48
+ end
49
+
50
+ before do
51
+ allow(PgEngine::Mailgun::LogSync).to receive(:download).and_return([])
52
+ end
53
+
54
+ it do
55
+ subject
56
+ expect(response).to have_http_status(:redirect)
57
+ end
58
+ end
59
+
60
+ describe 'routing' do
61
+ it 'routes GET index correctly' do
62
+ route = { get: '/a/email_logs' }
63
+ expect(route).to route_to(controller: 'admin/email_logs', action: 'index')
64
+ end
65
+ end
66
+
67
+ describe 'GET #index' do
68
+ subject do
69
+ get :index, params: {}
70
+ end
71
+
72
+ before { create :email_log }
73
+
74
+ it 'returns a success response' do
75
+ subject
76
+ expect(response).to be_successful
77
+ end
78
+
79
+ context 'when user is not logged in' do
80
+ let(:logged_user) { nil }
81
+
82
+ it 'redirects to login path' do
83
+ subject
84
+ expect(response).to redirect_to(new_user_session_path)
85
+ end
86
+ end
87
+
88
+ context 'when se pide el excel' do
89
+ subject do
90
+ get :index, params: {}, format: 'xlsx'
91
+ end
92
+
93
+ it 'returns a success response' do
94
+ subject
95
+ expect(response).to be_successful
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'GET #show' do
101
+ it 'returns a success response' do
102
+ email_log = create(:email_log)
103
+ get :show, params: { id: email_log.to_param }
104
+ expect(response).to be_successful
105
+ end
106
+ end
107
+
108
+ describe 'GET #new' do
109
+ it 'returns a success response' do
110
+ get :new, params: {}
111
+ expect(response).to be_successful
112
+ end
113
+ end
114
+
115
+ describe 'GET #edit' do
116
+ it 'returns a success response' do
117
+ email_log = create(:email_log)
118
+ get :edit, params: { id: email_log.to_param }
119
+ expect(response).to be_successful
120
+ end
121
+ end
122
+
123
+ describe 'POST #create' do
124
+ context 'with valid params' do
125
+ it 'creates a new EmailLog' do
126
+ expect do
127
+ post :create, params: { email_log: valid_attributes }
128
+ end.to change(EmailLog, :count).by(1)
129
+ end
130
+
131
+ it 'redirects to the created email_log' do
132
+ post :create, params: { email_log: valid_attributes }
133
+ expect(response).to redirect_to([:admin, EmailLog.last])
134
+ end
135
+ end
136
+ end
137
+
138
+ describe 'PUT #update' do
139
+ context 'with valid params' do
140
+ let(:new_attributes) do
141
+ attributes_for(:email_log)
142
+ end
143
+
144
+ it 'redirects to the email_log' do
145
+ email_log = create(:email_log)
146
+ put :update, params: { id: email_log.to_param, email_log: valid_attributes }
147
+ expect(response).to redirect_to([:admin, email_log])
148
+ end
149
+ end
150
+ end
151
+
152
+ describe 'DELETE #destroy' do
153
+ subject do
154
+ request.headers['Accept'] = 'text/vnd.turbo-stream.html,text/html'
155
+ delete :destroy, params: { id: email_log.to_param, redirect_to: redirect_url }
156
+ end
157
+
158
+ let!(:email_log) { create :email_log }
159
+ let(:redirect_url) { nil }
160
+
161
+ it 'destroys the requested email_log' do
162
+ expect { subject }.to change(EmailLog, :count).by(-1)
163
+ end
164
+
165
+ it 'quita el elemento de la lista' do
166
+ subject
167
+ expect(response.body).to include('turbo-stream action="remove"')
168
+ end
169
+
170
+ context 'si hay redirect_to' do
171
+ let(:redirect_url) { admin_email_logs_url }
172
+
173
+ it 'redirects to the email_logs list' do
174
+ subject
175
+ expect(response).to redirect_to(admin_email_logs_url)
176
+ end
177
+ end
178
+ end
179
+ end
@@ -60,6 +60,17 @@ describe DummyBaseController do
60
60
  expect(response.body).to include '<turbo-stream action="remove" targets=".modal">'
61
61
  end
62
62
  end
63
+
64
+ context 'cuando acepta json' do
65
+ before do
66
+ request.headers['Accept'] = 'application/json'
67
+ end
68
+
69
+ fit do
70
+ subject
71
+ expect(response).to have_http_status(:internal_server_error)
72
+ end
73
+ end
63
74
  end
64
75
 
65
76
  describe 'not_authorized' do
@@ -0,0 +1,142 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec::Matchers.define_negated_matcher :not_change, :change
4
+
5
+ def build_body(log_id, signature, timestamp)
6
+ <<~JSON
7
+ {
8
+ "signature": {
9
+ "timestamp": "#{timestamp}",
10
+ "token": "6fcc81a280a2458de8c83bf22dde8c71485be1d51333d3427e",
11
+ "signature": "#{signature}"
12
+ },
13
+ "event-data": {
14
+ "timestamp": 1715014542.2477064,
15
+ "ip": "66.102.8.333",
16
+ "event": "delivered",
17
+ "id": "#{log_id}",
18
+ "severity":"temporary",
19
+ "log-level": "info",
20
+ "message": {
21
+ "headers": { "message-id": "msgid@fakeapp2024.mail" }
22
+ },
23
+ "recipient": "natprobel@fakemail.com"
24
+ }
25
+ }
26
+ JSON
27
+ end
28
+
29
+ fdescribe Public::WebhooksController do
30
+ include ActiveSupport::Testing::TimeHelpers
31
+
32
+ before { travel_to Time.zone.at(1_716_564_587) }
33
+
34
+ describe '#mailgun' do
35
+ subject do
36
+ post :mailgun, body:, as: :json
37
+ end
38
+
39
+ let(:signature) { 'c524037907046276117758afae8a340e77a43a6e48eb35a9521426e7a3ff675b' }
40
+ let(:log_id) { 'log_2' }
41
+ let(:timestamp) { '1716564587' }
42
+
43
+ let(:body) do
44
+ build_body(log_id, signature, timestamp)
45
+ end
46
+
47
+ it do
48
+ subject
49
+ expect(response).to have_http_status(:ok)
50
+ end
51
+
52
+ it do
53
+ expect { subject }.to change(EmailLog, :count).by(1)
54
+ end
55
+
56
+ context 'cuando tira internal server error' do
57
+ let(:body) { '{ "este json": "no me sirve" }' }
58
+
59
+ it do
60
+ subject
61
+ expect(response).to have_http_status(:internal_server_error)
62
+ end
63
+
64
+ fit do
65
+ expect { subject }.to have_errored('internal server error')
66
+ .and(have_errored('no me sirve'))
67
+ end
68
+ end
69
+
70
+ context 'cuando no es un json' do
71
+ let(:body) { 'mal json' }
72
+
73
+ it do
74
+ subject
75
+ expect(response).to have_http_status(:bad_request)
76
+ end
77
+
78
+ fit do
79
+ expect { subject }.to have_warned('parser error').and(have_warned('mal json'))
80
+ end
81
+ end
82
+
83
+ context 'cuando ya existe un log con ese ID' do
84
+ before do
85
+ create(:email_log, log_id:)
86
+ end
87
+
88
+ it do
89
+ expect { subject }.to have_warned('ya existía un log con ese id')
90
+ .and(not_change(EmailLog, :count))
91
+ end
92
+ end
93
+
94
+ shared_context 'todo bien pero no guarda el log' do
95
+ it 'responds ok' do
96
+ subject
97
+ expect(response).to have_http_status(:ok)
98
+ end
99
+
100
+ it 'no guarda el log ;)' do
101
+ expect { subject }.not_to change(EmailLog, :count)
102
+ end
103
+ end
104
+
105
+ context 'si la signature no es válida' do
106
+ let(:signature) { 'no válida' }
107
+
108
+ it_behaves_like 'todo bien pero no guarda el log'
109
+
110
+ fit do
111
+ expect { subject }.to have_warned('refusing invalid signature')
112
+ end
113
+ end
114
+
115
+ context 'cuando ya se usó el token' do
116
+ subject do
117
+ post :mailgun, body: build_body('otro id', signature, timestamp), as: :json
118
+ end
119
+
120
+ before do
121
+ post :mailgun, body: build_body(log_id, signature, timestamp), as: :json
122
+ end
123
+
124
+ it_behaves_like 'todo bien pero no guarda el log'
125
+
126
+ fit do
127
+ expect { subject }.to have_warned('refusing used token')
128
+ end
129
+ end
130
+
131
+ context 'cuando la timestamp está muy lejos' do
132
+ let(:timestamp) { '1111111' }
133
+ let(:signature) { '869c06cea4be27321a6a7c6a8e3d0668bb9c2b5b3de5532f644dba181a157d9a' }
134
+
135
+ it_behaves_like 'todo bien pero no guarda el log'
136
+
137
+ fit do
138
+ expect { subject }.to have_warned('refusing due to timestamp')
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generado con pg_rails
4
+
5
+ FactoryBot.define do
6
+ factory :email_log do
7
+ email
8
+ log_id { Faker::Lorem.sentence }
9
+ event { %w[accepted delivered failed opened].sample }
10
+ log_level { %w[info warn error].sample }
11
+ severity { %w[permanent temporary].sample }
12
+ timestamp { '' }
13
+ message_id { Faker::Lorem.sentence }
14
+
15
+ trait :email_existente do
16
+ email { nil }
17
+ email_id { Email.pluck(:id).sample }
18
+ end
19
+ end
20
+ end
@@ -1,25 +1,99 @@
1
1
  require 'rails_helper'
2
2
  require 'fileutils'
3
3
 
4
- describe PgEngine::Mailgun::LogSync do
4
+ describe PgEngine::Mailgun::LogSync, vcr: { cassette_name: 'mailgun/log_sync_download',
5
+ match_requests_on: %i[method host] } do
5
6
  let(:instancia) { described_class }
6
7
 
7
- describe '#download',
8
- vcr: { cassette_name: 'mailgun/log_sync_download',
9
- match_requests_on: %i[method host] } do
8
+ describe '#download' do
10
9
  subject do
11
10
  instancia.download
12
- instancia.sync_redis
13
11
  end
14
12
 
15
13
  after do
16
- Dir["#{instancia.dir}/*.json"].each { |file| File.delete(file) }
14
+ FileUtils.rm_r(instancia.log_dir)
17
15
  end
18
16
 
19
17
  let!(:email) { create :email, message_id: '66393f1bc7d4_47a5108ec1628f@notebook.mail' }
20
18
 
21
19
  it do
22
- expect { subject }.to change { email.logs.to_a.length }.from(0).to(3)
20
+ expect { subject }.to change { email.email_logs.count }.from(0).to(3)
21
+ .and(change(EmailLog, :count).to(8))
22
+ end
23
+ end
24
+
25
+ describe '#digest' do
26
+ subject do
27
+ instancia.digest(JSON.parse(log_data))
28
+ end
29
+
30
+ let(:log_data) do
31
+ <<~JSON
32
+ {
33
+ "timestamp": 1715014542.2477064,
34
+ "ip": "66.102.8.333",
35
+ "event": "delivered",
36
+ "id": "log_2",
37
+ "severity":"temporary",
38
+ "log-level": "info",
39
+ "message": {
40
+ "headers": { "message-id": "msgid@fakeapp2024.mail" }
41
+ },
42
+ "recipient": "natprobel@fakemail.com"
43
+ }
44
+ JSON
45
+ end
46
+
47
+ let(:expected_attributes) do
48
+ {
49
+ log_id: 'log_2',
50
+ event: 'delivered',
51
+ log_level: 'info',
52
+ severity: 'temporary',
53
+ timestamp: 1_715_014_542,
54
+ message_id: 'msgid@fakeapp2024.mail'
55
+ }
56
+ end
57
+
58
+ it do
59
+ expect { subject }.to change(EmailLog, :count).by(1)
60
+ end
61
+
62
+ it do
63
+ expect(subject).to have_attributes(expected_attributes)
64
+ end
65
+
66
+ context 'ya existe el log' do
67
+ subject do
68
+ instancia.digest(JSON.parse(log_data))
69
+ instancia.digest(JSON.parse(log_data))
70
+ end
71
+
72
+ it 'solo lo crea una vez' do
73
+ expect { subject }.to change(EmailLog, :count).by(1)
74
+ end
75
+ end
76
+
77
+ context 'cuando se asocia a un email' do
78
+ let!(:email) { create :email, message_id: 'msgid@fakeapp2024.mail' }
79
+
80
+ it do
81
+ expect(subject.email).to eq email
82
+ end
83
+
84
+ it 'changes email status' do
85
+ expect { subject }.to change { email.reload.status }.to 'delivered'
86
+ end
87
+ end
88
+
89
+ context 'cuando hay errores' do
90
+ subject do
91
+ instancia.digest({})
92
+ end
93
+
94
+ it do
95
+ expect { subject }.to raise_error(NoMethodError)
96
+ end
23
97
  end
24
98
  end
25
99
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generado con pg_rails
4
+
5
+ require 'rails_helper'
6
+
7
+ RSpec.describe EmailLog do
8
+ let(:email_log) { create(:email_log) }
9
+
10
+ it 'se persiste' do
11
+ expect(email_log).to be_persisted
12
+ end
13
+
14
+ describe 'status_for_email' do
15
+ subject do
16
+ email_log.status_for_email
17
+ end
18
+
19
+ let(:email_log) { create(:email_log, event:, severity:) }
20
+ let(:severity) { nil }
21
+
22
+ context 'cuando es accepted' do
23
+ let(:event) { 'accepted' }
24
+
25
+ it { is_expected.to eq 'accepted' }
26
+ end
27
+
28
+ context 'cuando es delivered' do
29
+ let(:event) { 'delivered' }
30
+
31
+ it { is_expected.to eq 'delivered' }
32
+ end
33
+
34
+ context 'cuando hay un fallo temporario' do
35
+ let(:event) { 'failed' }
36
+ let(:severity) { 'temporary' }
37
+
38
+ it { is_expected.to eq 'accepted' }
39
+ end
40
+
41
+ context 'cuando hay un fallo permanente' do
42
+ let(:event) { 'failed' }
43
+ let(:severity) { 'permanent' }
44
+
45
+ it { is_expected.to eq 'rejected' }
46
+ end
47
+ end
48
+ end
@@ -10,4 +10,39 @@ RSpec.describe Email do
10
10
  it 'se persiste' do
11
11
  expect(email).to be_persisted
12
12
  end
13
+
14
+ describe 'update_status!' do
15
+ context 'cuando hay accepted y delivered' do
16
+ subject do
17
+ create :email_log, email: email.reload, event: 'accepted'
18
+ create :email_log, email: email.reload, event: 'delivered'
19
+ end
20
+
21
+ it do
22
+ expect { subject }.to change { email.reload.status }.to 'delivered'
23
+ end
24
+ end
25
+
26
+ context 'cuando hay accepted y fallo temporario' do
27
+ subject do
28
+ create :email_log, email: email.reload, event: 'failed', severity: 'temporary'
29
+ create :email_log, email: email.reload, event: 'accepted'
30
+ end
31
+
32
+ it do
33
+ expect { subject }.to change { email.reload.status }.to 'accepted'
34
+ end
35
+ end
36
+
37
+ context 'cuando hay accepted y fallo permanente' do
38
+ subject do
39
+ create :email_log, email: email.reload, event: 'accepted'
40
+ create :email_log, email: email.reload, event: 'failed', severity: 'permanent'
41
+ end
42
+
43
+ it do
44
+ expect { subject }.to change { email.reload.status }.to 'rejected'
45
+ end
46
+ end
47
+ end
13
48
  end
@@ -33,6 +33,17 @@ html
33
33
  - if @rollbar_token.present?
34
34
  meta name="rollbar-token" content="#{@rollbar_token}"
35
35
  meta name="rollbar-env" content="#{Rails.env}"
36
+
37
+ link rel="preconnect" href="https://fonts.googleapis.com"
38
+ link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="crossorigin"
39
+ link href="https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap" rel="stylesheet"
40
+ css:
41
+ body {
42
+ font-family: "Ubuntu", sans-serif;
43
+ font-weight: 400;
44
+ font-style: normal;
45
+ }
46
+
36
47
  body
37
48
  = render partial: 'pg_layout/sidebar_mobile'
38
49
 
@@ -0,0 +1,62 @@
1
+ # :nocov:
2
+ module PgEngine
3
+ module Matchers
4
+ module PgLogger
5
+ class Base < RSpec::Rails::Matchers::BaseMatcher
6
+ def initialize(text, level)
7
+ super()
8
+ @text = text
9
+ @level = level
10
+ end
11
+ end
12
+
13
+ class PgHaveLogged < Base
14
+ def matches?(proc)
15
+ msg = 'have_logged only support block expectations'
16
+ raise ArgumentError, msg unless proc.is_a?(Proc)
17
+
18
+ original_messages = Set.new(PgEngine::PgLogger.test_logged_messages)
19
+ proc.call
20
+ logged_messages = Set.new(PgEngine::PgLogger.test_logged_messages)
21
+
22
+ @new_messages = logged_messages - original_messages
23
+ @new_messages.any? do |level, message|
24
+ if @text.present? && @level.present?
25
+ level == @level && message.include?(@text)
26
+ elsif @text.present?
27
+ message.include? @text
28
+ elsif @level.present?
29
+ level == @level
30
+ else
31
+ true
32
+ end
33
+ end
34
+ end
35
+
36
+ def failure_message
37
+ msg = "expected to #{@level || log}"
38
+ msg << "with text: #{@text}" if @text.present?
39
+ return unless @new_messages.any?
40
+
41
+ msg << "\nLogged messages:"
42
+ @new_messages.each do |level, message|
43
+ msg << "\n #{level}: #{message[0..200]}"
44
+ end
45
+ end
46
+
47
+ def supports_block_expectations?
48
+ true
49
+ end
50
+ end
51
+ end
52
+
53
+ def have_errored(text = nil) # rubocop:disable Naming/PredicateName
54
+ PgLogger::PgHaveLogged.new(text, :error)
55
+ end
56
+
57
+ def have_warned(text = nil) # rubocop:disable Naming/PredicateName
58
+ PgLogger::PgHaveLogged.new(text, :warn)
59
+ end
60
+ end
61
+ end
62
+ # :nocov:
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.0.8-alpha.82'
4
+ VERSION = '7.0.8-alpha.84'
5
5
  end