Back to Repositories

Testing Base Notification System Implementation in bullet

This test suite validates the Base notification class functionality in the Bullet gem, focusing on core notification behaviors and user identification methods. The tests ensure proper error handling, environment variable processing, and notification delivery mechanisms.

Test Coverage Overview

The test suite provides comprehensive coverage of the Bullet::Notification::Base class functionality.

Key areas tested include:
  • Error handling for abstract methods (title and body)
  • User identification through whoami functionality
  • Notification data assembly and delivery
  • Environment variable handling
  • Call stack message integration

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development patterns to validate the notification system’s core components.

Notable implementation patterns include:
  • Context-based test organization
  • Extensive use of mock objects and method stubbing
  • Environment variable manipulation helpers
  • Error condition simulation

Technical Details

Testing infrastructure includes:
  • RSpec as the primary testing framework
  • Double objects for mock testing
  • Environment variable manipulation helpers
  • System command integration testing
  • Custom helper methods for temporary environment modifications

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test contexts with clear separation of concerns
  • Proper cleanup of environment modifications
  • Comprehensive error case coverage
  • Effective use of RSpec’s expect syntax
  • Clean and maintainable test organization

flyerhzm/bullet

spec/bullet/notification/base_spec.rb

            
# frozen_string_literal: true

require 'spec_helper'

module Bullet
  module Notification
    describe Base do
      subject { Base.new(Post, %i[comments votes]) }

      context '#title' do
        it 'should raise NoMethodError' do
          expect { subject.title }
            .to raise_error(NoMethodError)
        end
      end

      context '#body' do
        it 'should raise NoMethodError' do
          expect { subject.body }
            .to raise_error(NoMethodError)
        end
      end

      context '#whoami' do
        it 'should display user name' do
          user = `whoami`.chomp
          expect(subject.whoami).to eq("user: #{user}")
        end

        it 'should leverage ENV parameter' do
          temp_env_variable('USER', 'bogus') { expect(subject.whoami).to eq('user: bogus') }
        end

        it 'should return blank if no user available' do
          temp_env_variable('USER', '') do
            expect(subject).to receive(:`).with('whoami').and_return('')
            expect(subject.whoami).to eq('')
          end
        end

        it 'should return blank if whoami is not available' do
          temp_env_variable('USER', '') do
            expect(subject).to receive(:`).with('whoami').and_raise(Errno::ENOENT)
            expect(subject.whoami).to eq('')
          end
        end

        def temp_env_variable(name, value)
          old_value = ENV[name]
          ENV[name] = value
          yield
        ensure
          ENV[name] = old_value
        end
      end

      context '#body_with_caller' do
        it 'should return body' do
          allow(subject).to receive(:body).and_return('body')
          allow(subject).to receive(:call_stack_messages).and_return('call_stack_messages')
          expect(subject.body_with_caller).to eq("body
call_stack_messages
")
        end
      end

      context '#notification_data' do
        it 'should return notification data' do
          allow(subject).to receive(:whoami).and_return('whoami')
          allow(subject).to receive(:url).and_return('url')
          allow(subject).to receive(:title).and_return('title')
          allow(subject).to receive(:body_with_caller).and_return('body_with_caller')
          expect(subject.notification_data).to eq(user: 'whoami', url: 'url', title: 'title', body: 'body_with_caller')
        end

        context 'when skip_user_in_notification is true' do
          before { allow(Bullet).to receive(:skip_user_in_notification).and_return(true) }

          it 'should return notification data without user' do
            allow(subject).to receive(:url).and_return('url')
            allow(subject).to receive(:title).and_return('title')
            allow(subject).to receive(:body_with_caller).and_return('body_with_caller')

            expect(subject.notification_data).to eq(url: 'url', title: 'title', body: 'body_with_caller')
          end
        end
      end

      context '#notify_inline' do
        it 'should send full_notice to notifier' do
          notifier = double
          allow(subject).to receive(:notifier).and_return(notifier)
          allow(subject).to receive(:notification_data).and_return({ foo: :bar })
          expect(notifier).to receive(:inline_notify).with({ foo: :bar })
          subject.notify_inline
        end
      end

      context '#notify_out_of_channel' do
        it 'should send full_out_of_channel to notifier' do
          notifier = double
          allow(subject).to receive(:notifier).and_return(notifier)
          allow(subject).to receive(:notification_data).and_return({ foo: :bar })
          expect(notifier).to receive(:out_of_channel_notify).with({ foo: :bar })
          subject.notify_out_of_channel
        end
      end
    end
  end
end