From 8c084e0faa971e6db265ea39aefb8cf07e39de43 Mon Sep 17 00:00:00 2001 From: mms Date: Thu, 14 Nov 2024 22:34:47 +0100 Subject: chore: add specs for all classes --- .rubocop.yml | 10 +++++++ .rubocop_todo.yml | 32 +++++++++++++++++++++ bin/chotto | 1 + lib/chotto.rb | 10 +++---- lib/chotto/config.rb | 2 +- lib/chotto/database.rb | 10 ++----- lib/chotto/message.rb | 14 ++++++---- lib/chotto/ruleset.rb | 6 ++-- lib/chotto/tags.rb | 16 ----------- spec/chotto_spec.rb | 52 ++++++++++++++++++++++++++++++++++ spec/lib/database_spec.rb | 31 +++++++++++++++++++++ spec/lib/helpers_spec.rb | 15 ++++++++++ spec/lib/message_spec.rb | 46 ++++++++++++++++++++++++++++++ spec/lib/messages_spec.rb | 71 +++++++++++++++++++++++++++++++++++++++++++++++ spec/lib/ruleset_spec.rb | 27 ++++++++++++++++++ spec/message_spec.rb | 46 ------------------------------ spec/messages_spec.rb | 71 ----------------------------------------------- 17 files changed, 305 insertions(+), 155 deletions(-) create mode 100644 .rubocop.yml create mode 100644 .rubocop_todo.yml delete mode 100644 lib/chotto/tags.rb create mode 100644 spec/chotto_spec.rb create mode 100644 spec/lib/database_spec.rb create mode 100644 spec/lib/helpers_spec.rb create mode 100644 spec/lib/message_spec.rb create mode 100644 spec/lib/messages_spec.rb create mode 100644 spec/lib/ruleset_spec.rb delete mode 100644 spec/message_spec.rb delete mode 100644 spec/messages_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..ec287a1 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,10 @@ + +AllCops: + NewCops: enable + +Metrics/BlockLength: + Exclude: + - 'spec/**/*' + + +inherit_from: .rubocop_todo.yml diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..a1404a9 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,32 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2024-11-14 21:18:09 UTC using RuboCop version 1.68.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 7 +# Configuration parameters: AllowedConstants. +Style/Documentation: + Exclude: + - 'spec/**/*' + - 'test/**/*' + - 'lib/chotto.rb' + - 'lib/chotto/config.rb' + - 'lib/chotto/database.rb' + - 'lib/chotto/helpers.rb' + - 'lib/chotto/message.rb' + - 'lib/chotto/messages.rb' + - 'lib/chotto/tags.rb' + +# Offense count: 2 +# Configuration parameters: AllowedVariables. +Style/GlobalVars: + Exclude: + - 'spec/chotto_spec.rb' + +# Offense count: 1 +Style/MissingRespondToMissing: + Exclude: + - 'lib/chotto/message.rb' diff --git a/bin/chotto b/bin/chotto index 3d66d4e..61cfc75 100755 --- a/bin/chotto +++ b/bin/chotto @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require_relative '../lib/chotto' diff --git a/lib/chotto.rb b/lib/chotto.rb index 6e1f908..71a1ac8 100644 --- a/lib/chotto.rb +++ b/lib/chotto.rb @@ -9,7 +9,6 @@ require_relative 'chotto/helpers' require_relative 'chotto/message' require_relative 'chotto/messages' require_relative 'chotto/ruleset' -require_relative 'chotto/tags' module Chotto class << self @@ -18,15 +17,16 @@ module Chotto def configure(&block) @config ||= Config.new + @config.db_class = ::Notmuch::Database @rule_sets = [] instance_eval(&block) - @db = Database.new(path: config.database_path) + @db = Database.new(path: config.database_path, db_class: config.db_class) end def rule_set(name, &block) - @rule_sets << RuleSet.new(name, block) + @rule_sets << RuleSet.new(name, db, block) end def close_db @@ -36,8 +36,6 @@ module Chotto end def eval_rules - Chotto.rule_sets.each do |rule_set| - rule_set.run - end + Chotto.rule_sets.each(&:run) Chotto.close_db end diff --git a/lib/chotto/config.rb b/lib/chotto/config.rb index 267e72c..2edfcc8 100644 --- a/lib/chotto/config.rb +++ b/lib/chotto/config.rb @@ -2,6 +2,6 @@ module Chotto class Config - attr_accessor :database_path + attr_accessor :database_path, :db_class end end diff --git a/lib/chotto/database.rb b/lib/chotto/database.rb index 893c970..a197588 100644 --- a/lib/chotto/database.rb +++ b/lib/chotto/database.rb @@ -4,16 +4,12 @@ module Chotto class Database attr_reader :db - def initialize(path:) - @db = ::Notmuch::Database.new(path, mode: Notmuch::MODE_READ_WRITE) - end - - def query(query) - db.query(query) + def initialize(path:, db_class:) + @db = db_class.new(path, mode: Notmuch::MODE_READ_WRITE) end def search_messages(query) - query(query).search_messages + db.query(query).search_messages end def close diff --git a/lib/chotto/message.rb b/lib/chotto/message.rb index ff382f8..282a030 100644 --- a/lib/chotto/message.rb +++ b/lib/chotto/message.rb @@ -11,18 +11,20 @@ module Chotto end def method_missing(method_name, *_args) - handle_get(Chotto::Helpers.header_name_from_dsl(method_name)) - end - - def handle_get(header_name) - message.header(header_name) if message.header(header_name) + handle_get_header(Chotto::Helpers.header_name_from_dsl(method_name)) end def save! message.remove_all_tags tags.each do |tag| - message.add_tag(tag) + message.add_tag(tag.to_s) end end + + private + + def handle_get_header(header_name) + message.header(header_name) + end end end diff --git a/lib/chotto/ruleset.rb b/lib/chotto/ruleset.rb index 289aa2f..08c9dbd 100644 --- a/lib/chotto/ruleset.rb +++ b/lib/chotto/ruleset.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + module Chotto - RuleSet = Struct.new(:name, :rule) do + RuleSet = Struct.new(:name, :db, :rule) do def messages - Messages.new(db: Chotto.db) + Messages.new(db: db) end def run diff --git a/lib/chotto/tags.rb b/lib/chotto/tags.rb deleted file mode 100644 index 52caeec..0000000 --- a/lib/chotto/tags.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Chotto - class Tags - include Enumerable - - attr_accessor :tags, :message - - def initialize(tags:, message:) - @tags = tags - @messae = message - end - - def each(&block) - tags.each(&block) - end - end -end diff --git a/spec/chotto_spec.rb b/spec/chotto_spec.rb new file mode 100644 index 0000000..9e8650b --- /dev/null +++ b/spec/chotto_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rspec' +require_relative '../lib/chotto' + +describe Chotto do + let(:db_double) { double('::Notmuch::Database') } + let(:db_instance_double) { double('::Notmuch::Database') } + let(:db_query_double) { double('::Notmuch::Query') } + let(:message_double) { double('::Notmuch::Message') } + + before do + $db_double = db_double # we need to access this double within block evaled elsewhere + + allow(db_double).to receive(:new).with('/path', mode: Notmuch::MODE_READ_WRITE).and_return(db_instance_double) + + allow(db_instance_double).to receive(:query).with(' (from:baltar@galactica.com)').and_return(db_query_double) + allow(db_query_double).to receive(:search_messages).and_return([message_double]) + + Chotto.configure do + config.database_path = '/path' + config.db_class = $db_double + end + + Chotto.rule_set 'sample' do + messages.or.filter(from: 'baltar@galactica.com').each do |msg| + msg.tags = msg.subject == 'Number Six' ? [:todo] : [:spam] + msg.save! + end + end + end + + it 'sends the rules to Notmuch - scenario 1' do + expect(message_double).to receive(:tags).and_return([]) + expect(message_double).to receive(:header).with('Subject').and_return('Number Six') + expect(message_double).to receive(:remove_all_tags) + expect(message_double).to receive(:add_tag).with('todo') + expect(db_instance_double).to receive(:close) + + eval_rules + end + + it 'sends the rules to Notmuch - scenario 2' do + expect(message_double).to receive(:tags).and_return([]) + expect(message_double).to receive(:header).with('Subject').and_return('Boomer') + expect(message_double).to receive(:remove_all_tags) + expect(message_double).to receive(:add_tag).with('spam') + expect(db_instance_double).to receive(:close) + + eval_rules + end +end diff --git a/spec/lib/database_spec.rb b/spec/lib/database_spec.rb new file mode 100644 index 0000000..8da04e4 --- /dev/null +++ b/spec/lib/database_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'rspec' +require_relative '../../lib/chotto/database' + +RSpec.describe Chotto::Database do + let(:db_double) { double('Notmuch::Database') } + let(:db_instance_double) { double('Notmuch::Database') } + let(:query_double) { double('Notmuch::Query') } + let(:query) { 'from:baltar@battlestar.com' } + let(:path) { '/var/db/mail' } + let(:subject) { Chotto::Database } + + before do + expect(db_double).to receive(:new).with(path, mode: Notmuch::MODE_READ_WRITE).and_return(db_instance_double) + end + + it 'initializes the database instance' do + subject.new(path: path, db_class: db_double) + end + + describe '#search_messages' do + it 'uses the correct method chain' do + expect(db_instance_double).to receive(:query).with(query).and_return(query_double) + expect(query_double).to receive(:search_messages) + + db = subject.new(path: path, db_class: db_double) + db.search_messages(query) + end + end +end diff --git a/spec/lib/helpers_spec.rb b/spec/lib/helpers_spec.rb new file mode 100644 index 0000000..5b841e4 --- /dev/null +++ b/spec/lib/helpers_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rspec' +require_relative '../../lib/chotto/helpers' + +RSpec.describe Chotto::Helpers do + let(:subject) { Chotto::Helpers } + describe '#header_name_from_dsl' do + it { expect(subject.header_name_from_dsl('word')).to eq('Word') } + it { expect(subject.header_name_from_dsl('wOrd')).to eq('Word') } + it { expect(subject.header_name_from_dsl('Word')).to eq('Word') } + + it { expect(subject.header_name_from_dsl('many_words')).to eq('Many-Words') } + end +end diff --git a/spec/lib/message_spec.rb b/spec/lib/message_spec.rb new file mode 100644 index 0000000..2605117 --- /dev/null +++ b/spec/lib/message_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'rspec' +require_relative '../../lib/chotto/message' +require_relative '../../lib/chotto/helpers' + +RSpec.describe Chotto::Message do + let(:tags) { [] } + let(:msg) { double('Notmuch::Message', tags: tags) } + + let(:subject) { Chotto::Message.new(msg: msg) } + + it 'reads headers' do + expect(msg).to receive(:header).with('A-Header') + + subject.a_header + end + + describe 'tags' do + it 'keeps tags state in memory' do + subject.tags << 'tag' + expect(subject.tags).to match ['tag'] + + subject.tags << 'tag2' + expect(subject.tags).to match %w[tag tag2] + end + + it 'allows to overwritte entire tags array' do + subject.tags = [1, 2, 3, 4] + expect(subject.tags).to match [1, 2, 3, 4] + end + end + + describe 'save!' do + let(:tags) { [1, 2, 3] } + it 'saves current tag array to db' do + expect(msg).to receive(:remove_all_tags) + tags.each do |tag| + expect(msg).to receive(:add_tag).with(tag.to_s) + end + + subject.tags = tags + subject.save! + end + end +end diff --git a/spec/lib/messages_spec.rb b/spec/lib/messages_spec.rb new file mode 100644 index 0000000..7550991 --- /dev/null +++ b/spec/lib/messages_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'rspec' +require_relative '../../lib/chotto/messages' + +RSpec.describe Chotto::Messages do + let(:subject) { Chotto::Messages.new(db: double('Notmuch')) } + + describe 'direct filters' do + it { expect(subject.filter('from:baltar@battlestar.com').query_string).to eq(' (from:baltar@battlestar.com)') } + + it { + expect(subject.filter('from:baltar@battlestar.com').filter('ship:galactica').query_string) + .to eq(' (from:baltar@battlestar.com) and (ship:galactica)') + } + end + + describe 'conjuction change' do + it { + expect(subject.filter('from:baltar@battlestar.com') + .or.filter('ship:galactica').query_string).to eq(' (from:baltar@battlestar.com) or (ship:galactica)') + } + + it { + expect(subject.filter('from:baltar@battlestar.com') + .or.filter('ship:galactica') + .filter('hair:long').query_string) + .to eq(' (from:baltar@battlestar.com) or (ship:galactica) or (hair:long)') + } + + it { + expect(subject.filter('from:baltar@battlestar.com') + .or.filter('ship:galactica') + .and.filter('hair:long').query_string) + .to eq(' (from:baltar@battlestar.com) or (ship:galactica) and (hair:long)') + } + end + + describe 'hash filters' do + context 'with singular values' do + it { expect(subject.filter(from: 'baltar@battlestar.com').query_string).to eq(' (from:baltar@battlestar.com)') } + + it { + expect(subject.filter(from: 'baltar@battlestar.com', ship: 'galactica').query_string) + .to eq(' (from:baltar@battlestar.com) and (ship:galactica)') + } + + it { + expect(subject.or.filter(from: 'baltar@battlestar.com', ship: 'galactica').query_string) + .to eq(' (from:baltar@battlestar.com) or (ship:galactica)') + } + end + + context 'with set of values' do + it { + expect(subject.filter(from: ['baltar@battlestar.com', + 'adama@battestar.com']).query_string) + .to eq(' (from:baltar@battlestar.com) or (from:adama@battestar.com)') + } + end + end + + context 'advanced filters' do + it { + expect(subject.filter(from: ['baltar@battlestar.com', 'adama@battestar.com']) + .and.filter(ship: 'battlestar') + .or.filter(ship: 'pegasus').query_string) + .to eq(' (from:baltar@battlestar.com) or (from:adama@battestar.com) and (ship:battlestar) or (ship:pegasus)') + } + end +end diff --git a/spec/lib/ruleset_spec.rb b/spec/lib/ruleset_spec.rb new file mode 100644 index 0000000..e7a6904 --- /dev/null +++ b/spec/lib/ruleset_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rspec' +require_relative '../../lib/chotto/ruleset' +require_relative '../../lib/chotto/messages' + +RSpec.describe Chotto::RuleSet do + let(:db) { instance_double('Chotto::Database') } + let(:messages) { double('Chotto::Messages', mth: true) } + let(:rule) do + proc do + messages.mth + end + end + let(:subject) do + Chotto::RuleSet.new('a name', db, rule) + end + + describe '#run' do + it 'evaluates the run block' do + expect(Chotto::Messages).to receive(:new).with(db: db).and_return(messages) + expect(messages).to receive(:mth) + + subject.run + end + end +end diff --git a/spec/message_spec.rb b/spec/message_spec.rb deleted file mode 100644 index 5564d34..0000000 --- a/spec/message_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'rspec' -require_relative '../lib/chotto/message' -require_relative '../lib/chotto/helpers' - -RSpec.describe Chotto::Message do - let(:tags) { [] } - let(:msg) { double('Notmuch::Message', tags: tags) } - - let(:subject) { Chotto::Message.new(msg: msg) } - - it 'reads headers' do - expect(msg).to receive(:header).with('A-Header') - - subject.a_header - end - - describe 'tags' do - it 'keeps tags state in memory' do - subject.tags << 'tag' - expect(subject.tags).to match ['tag'] - - subject.tags << 'tag2' - expect(subject.tags).to match %w[tag tag2] - end - - it 'allows to overwritte entire tags array' do - subject.tags = [1, 2, 3, 4] - expect(subject.tags).to match [1, 2, 3, 4] - end - end - - describe 'save!' do - let(:tags) { [1, 2, 3] } - it 'saves current tag array to db' do - expect(msg).to receive(:remove_all_tags) - tags.each do |tag| - expect(msg).to receive(:add_tag).with(tag) - end - - subject.tags = tags - subject.save! - end - end -end diff --git a/spec/messages_spec.rb b/spec/messages_spec.rb deleted file mode 100644 index ec43379..0000000 --- a/spec/messages_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -require 'rspec' -require_relative '../lib/chotto/messages' - -RSpec.describe Chotto::Messages do - let(:subject) { Chotto::Messages.new(db: double('Notmuch')) } - - describe 'direct filters' do - it { expect(subject.filter('from:baltar@battlestar.com').query_string).to eq(' (from:baltar@battlestar.com)') } - - it { - expect(subject.filter('from:baltar@battlestar.com').filter('ship:galactica').query_string) - .to eq(' (from:baltar@battlestar.com) and (ship:galactica)') - } - end - - describe 'conjuction change' do - it { - expect(subject.filter('from:baltar@battlestar.com') - .or.filter('ship:galactica').query_string).to eq(' (from:baltar@battlestar.com) or (ship:galactica)') - } - - it { - expect(subject.filter('from:baltar@battlestar.com') - .or.filter('ship:galactica') - .filter('hair:long').query_string) - .to eq(' (from:baltar@battlestar.com) or (ship:galactica) or (hair:long)') - } - - it { - expect(subject.filter('from:baltar@battlestar.com') - .or.filter('ship:galactica') - .and.filter('hair:long').query_string) - .to eq(' (from:baltar@battlestar.com) or (ship:galactica) and (hair:long)') - } - end - - describe 'hash filters' do - context 'with singular values' do - it { expect(subject.filter(from: 'baltar@battlestar.com').query_string).to eq(' (from:baltar@battlestar.com)') } - - it { - expect(subject.filter(from: 'baltar@battlestar.com', ship: 'galactica').query_string) - .to eq(' (from:baltar@battlestar.com) and (ship:galactica)') - } - - it { - expect(subject.or.filter(from: 'baltar@battlestar.com', ship: 'galactica').query_string) - .to eq(' (from:baltar@battlestar.com) or (ship:galactica)') - } - end - - context 'with set of values' do - it { - expect(subject.filter(from: ['baltar@battlestar.com', - 'adama@battestar.com']).query_string) - .to eq(' (from:baltar@battlestar.com) or (from:adama@battestar.com)') - } - end - end - - context 'advanced filters' do - it { - expect(subject.filter(from: ['baltar@battlestar.com', 'adama@battestar.com']) - .and.filter(ship: 'battlestar') - .or.filter(ship: 'pegasus').query_string) - .to eq(' (from:baltar@battlestar.com) or (from:adama@battestar.com) and (ship:battlestar) or (ship:pegasus)') - } - end -end -- cgit v1.2.3