ses_machine 0.0.1.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +41 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +13 -0
  7. data/Rakefile +23 -0
  8. data/app/controllers/application_controller.rb +3 -0
  9. data/app/controllers/ses_machine_controller.rb +64 -0
  10. data/app/helpers/ses_machine_helper.rb +8 -0
  11. data/app/views/layouts/ses_machine/application.html.erb +20 -0
  12. data/app/views/ses_machine/activity.html.erb +37 -0
  13. data/app/views/ses_machine/index.html.erb +17 -0
  14. data/app/views/ses_machine/show_message.html.erb +76 -0
  15. data/generators/ses_machine/USAGE +3 -0
  16. data/generators/ses_machine/ses_machine_generator.rb +19 -0
  17. data/generators/ses_machine/templates/README +7 -0
  18. data/generators/ses_machine/templates/ses_machine.css +14 -0
  19. data/generators/ses_machine/templates/ses_machine.rb +19 -0
  20. data/generators/ses_machine/templates/ses_machine.yml +15 -0
  21. data/generators/ses_machine/templates/ses_machine_hooks.rb +33 -0
  22. data/init.rb +3 -0
  23. data/install.rb +3 -0
  24. data/lib/ses_machine/bounce.rb +16 -0
  25. data/lib/ses_machine/config.rb +107 -0
  26. data/lib/ses_machine/db.rb +92 -0
  27. data/lib/ses_machine/errors.rb +24 -0
  28. data/lib/ses_machine/mailer.rb +68 -0
  29. data/lib/ses_machine/routing.rb +8 -0
  30. data/lib/ses_machine/version.rb +13 -0
  31. data/lib/ses_machine.rb +88 -0
  32. data/lib/tasks/db.rake +28 -0
  33. data/lib/tasks/ses_machine.rake +91 -0
  34. data/public/stylesheets/all.css +0 -0
  35. data/ses_machine.gemspec +29 -0
  36. data/test/config_test.rb +34 -0
  37. data/test/db_test.rb +50 -0
  38. data/test/fixtures/config/ses_machine.yml +11 -0
  39. data/test/routing_test.rb +17 -0
  40. data/test/ses_machine_controller_test.rb +49 -0
  41. data/test/ses_machine_generator_test.rb +38 -0
  42. data/test/ses_machine_test.rb +10 -0
  43. data/test/test_helper.rb +7 -0
  44. data/uninstall.rb +3 -0
  45. metadata +193 -0
@@ -0,0 +1,92 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+
4
+ module SesMachine #:nodoc:
5
+ class DB #:nodoc:
6
+ class << self
7
+
8
+ def get_keywords(array)
9
+ array.map{|w| w.downcase.to_s}.uniq.delete_if{|w| w.size < 3}
10
+ end
11
+
12
+ def update_daily_stats
13
+ map = <<-JS
14
+ function() {
15
+ var self = this;
16
+ var key = this.date - (this.date % 86400000);
17
+ var value = {total: 1};
18
+
19
+ types.forEach(function(type) {
20
+ value[type] = self.bounce_type == type ? 1 : 0;
21
+ });
22
+
23
+ emit(key, value);
24
+ }
25
+ JS
26
+
27
+ reduce = <<-JS
28
+ function(key, values) {
29
+ var sum = {};
30
+ var total = 0;
31
+ types.forEach(function(type) {
32
+ sum[type] = 0;
33
+ });
34
+
35
+ values.forEach(function(value) {
36
+ for(var type in value) {
37
+ sum[type] += value[type];
38
+ }
39
+ });
40
+
41
+ types.forEach(function(type) {
42
+ total += sum[type];
43
+ });
44
+ sum['total'] = total;
45
+
46
+ return sum;
47
+ }
48
+ JS
49
+ t = SesMachine.database['mails']
50
+ t.map_reduce(map, reduce, :out => {'merge' => 'daily_stats'}, :raw => true, :scope => {'types' => SesMachine::Bounce::TYPES.values})
51
+ end
52
+
53
+ def update_monthly_stats
54
+ map = <<-JS
55
+ function() {
56
+
57
+ var date = new Date(this['_id'])
58
+
59
+ var key = {
60
+ year: date.getFullYear(),
61
+ month: date.getMonth() + 1
62
+ };
63
+ var value = this.value;
64
+
65
+ emit(key, value);
66
+ }
67
+ JS
68
+
69
+ reduce = <<-JS
70
+ function(key, values) {
71
+ var sum = {total: 0};
72
+
73
+ types.forEach(function(type) {
74
+ sum[type] = 0;
75
+ });
76
+
77
+ values.forEach(function(value) {
78
+ for(var type in value) {
79
+ sum[type] += value[type];
80
+ }
81
+ });
82
+
83
+ return sum;
84
+ }
85
+ JS
86
+ t = SesMachine.database['daily_stats']
87
+ t.map_reduce(map, reduce, :out => {'merge' => 'monthly_stats'}, :raw => true, :scope => {'types' => SesMachine::Bounce::TYPES.values})
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+
4
+ module SesMachine #:nodoc
5
+ module Errors #:nodoc
6
+
7
+ # Raised when the database connection has not been set up properly, either
8
+ # by attempting to set an object on the db that is not a +Mongo::DB+, or
9
+ # not setting anything at all.
10
+ #
11
+ # Example:
12
+ #
13
+ # <tt>InvalidDatabase.new("Not a DB")</tt>
14
+ class InvalidDatabase < StandardError
15
+ def initialize(database)
16
+ @database = database
17
+ end
18
+
19
+ def message
20
+ "Database should be a Mongo::DB, not #{@database.class.name}"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,68 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+
4
+ module SesMachine #:nodoc:
5
+ class Mailer #:nodoc:
6
+
7
+ def initialize(*args); end
8
+
9
+ def perform_delivery_ses_machine(mail)
10
+ raw_source = SesMachine.use_dkim? ? sign_mail(mail) : mail.encoded
11
+
12
+ begin
13
+ response = SesMachine.ses.send_raw_email(raw_source, :source => SesMachine.email_account)
14
+ rescue AWS::SES::ResponseError => e
15
+ response = e.response
16
+ end
17
+
18
+ doc = {
19
+ :address => mail.to,
20
+ :subject => mail.subject,
21
+ :raw_source => raw_source,
22
+ :date => response.headers['date'].to_time.utc,
23
+ :request_id => response.request_id,
24
+ :response => response.to_s,
25
+ :response_code => response.code,
26
+ :response_error => response.error.to_s,
27
+ :bounce_type => response.error? ? SesMachine::Bounce::TYPES[:unknown] : 0,
28
+ '_keywords' => SesMachine::DB.get_keywords([mail.to].flatten + mail.subject.split)
29
+ }
30
+
31
+ doc.merge!(:message_id => response.message_id) unless response.error?
32
+
33
+ SesMachine.database['mails'].insert(doc)
34
+ end
35
+
36
+ alias_method :deliver!, :perform_delivery_ses_machine
37
+
38
+ protected
39
+
40
+ def dkim_signer
41
+ key = File.readlines(SesMachine.dkim_private_key).join
42
+ DKIM::Signer.new(SesMachine.dkim_domain, SesMachine.dkim_selector, key)
43
+ end
44
+
45
+ def sign_mail(mail)
46
+ # Read more: http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/DKIM.html
47
+ exclude_headers = ['Message-Id', 'Date', 'Return-Path', 'Bounces-To']
48
+ exclude_headers_rxp = /^(#{exclude_headers.join('|')}):/i
49
+ signer = dkim_signer
50
+ raw_source = ''
51
+ mail.encoded.each_line do |line|
52
+ unless line =~ exclude_headers_rxp
53
+ unless line.blank?
54
+ line.gsub!(/\n/, "\r\n")
55
+ line.gsub!(/\r+/, "\r")
56
+ end
57
+ signer.feed(line)
58
+ raw_source << line
59
+ end
60
+ end
61
+ signature = signer.finish
62
+ "#{signature.signature_header}\r\n#{raw_source}"
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+ ActionMailer::Base.add_delivery_method :ses_machine, SesMachine::Mailer
@@ -0,0 +1,8 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+
4
+ Rails.application.routes.draw do
5
+ match "ses_machine" => "ses_machine#index", :as => :ses_machine
6
+ match "ses_machine/activity" => "ses_machine#activity", :as => :ses_machine_activity
7
+ match "ses_machine/message/:id" => "ses_machine#show_message", :as => :ses_machine_message
8
+ end
@@ -0,0 +1,13 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+
4
+ module SesMachine #:nodoc
5
+ module VERSION #:nodoc:
6
+ MAJOR = 0
7
+ MINOR = 0
8
+ TINY = 1
9
+ PRE = 'dev'
10
+
11
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
12
+ end
13
+ end
@@ -0,0 +1,88 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+
4
+ lib = File.expand_path(File.dirname(__FILE__))
5
+ $:.unshift lib unless $:.include?(lib)
6
+
7
+ # require 'rubygems'
8
+
9
+ # gem 'rails', '~> 2.3.8'
10
+ # gem 'activesupport', '~> 2.3.8'
11
+ # gem 'actionmailer', '~> 2.3.8'
12
+ # gem 'will_paginate', '~> 2.3'
13
+
14
+ require 'singleton'
15
+ require 'mongo'
16
+ require 'action_mailer'
17
+ require 'action_mailer/version'
18
+ require 'dkim'
19
+ require 'mail'
20
+ require 'aws/ses'
21
+ require 'will_paginate'
22
+ require 'ses_machine/bounce'
23
+ require 'ses_machine/config'
24
+ require 'ses_machine/db'
25
+ require 'ses_machine/errors'
26
+ require 'ses_machine/mailer'
27
+ require 'ses_machine/routing'
28
+
29
+ %w{ models controllers helpers}.each do |dir|
30
+ path = File.join(File.dirname(__FILE__), '..', 'app', dir)
31
+ $LOAD_PATH << path
32
+ # ActiveSupport::Dependencies.load_paths << path
33
+ # ActiveSupport::Dependencies.load_once_paths.delete(path)
34
+ # ActionController::Base.append_view_path(File.join(File.dirname(__FILE__), '..', 'app', 'views'))
35
+ end
36
+
37
+ # WillPaginate.enable_actionpack
38
+ if defined?(::Rails::Railtie)
39
+ require 'will_paginate/railtie'
40
+ end
41
+
42
+ module SesMachine #:nodoc
43
+
44
+ module Hooks; end
45
+
46
+ class << self
47
+
48
+ # Sets the SesMachine configuration options. Best used by passing a block.
49
+ #
50
+ # Example:
51
+ #
52
+ # SesMachine.configure do |config|
53
+ # config.use_dkim = true
54
+ # config.dkim_domain = 'example.com'
55
+ # config.dkim_selector = 'ses'
56
+ # config.dkim_private_key = '/path/to/private/key'
57
+ # end
58
+ #
59
+ # Returns:
60
+ #
61
+ # The SesMachine +Config+ singleton instance.
62
+ def configure
63
+ SesMachine::Config.instance
64
+ config = SesMachine::Config.instance
65
+ block_given? ? yield(config) : config
66
+ end
67
+ alias :config :configure
68
+ end
69
+
70
+ # Take all the public instance methods from the Config singleton and allow
71
+ # them to be accessed through the SesMachine module directly.
72
+ #
73
+ # Example:
74
+ #
75
+ # <tt>SesMachine.database = Mongo::Connection.new.db("test")</tt>
76
+ SesMachine::Config.public_instance_methods(false).each do |name|
77
+ (class << self; self; end).class_eval <<-EOT
78
+ def #{name}(*args)
79
+ configure.send("#{name}", *args)
80
+ end
81
+ EOT
82
+ end
83
+ end
84
+
85
+ ENV['RAILS_ENV'] ||= Rails.env || 'development'
86
+ ENV['RAILS_ROOT'] ||= Rails.root.to_s || File.dirname(__FILE__) + '/../../../..'
87
+
88
+ SesMachine.config.load
data/lib/tasks/db.rake ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ namespace :ses_machine do
4
+ namespace :db do
5
+
6
+ desc 'Update daily statistics'
7
+ task :update_daily_stats => :environment do
8
+ response = SesMachine::DB.update_daily_stats
9
+ puts "DAILY STATS (#{response['timeMillis']}ms) - #{response['ok'] ? 'Success' : 'Failure'}"
10
+ end
11
+
12
+ desc 'Update monthly statistics'
13
+ task :update_monthly_stats => :environment do
14
+ response = SesMachine::DB.update_monthly_stats
15
+ puts "MONTHLY STATS (#{response['timeMillis']}ms) - #{response['ok'] ? 'Success' : 'Failure'}"
16
+ end
17
+
18
+ desc 'Create database indices'
19
+ task :create_indices => :environment do
20
+ t = SesMachine.database['mails']
21
+ t.ensure_index([['message_id', Mongo::ASCENDING]])
22
+ t.ensure_index([['bounce_type', Mongo::ASCENDING]])
23
+ t.ensure_index([['_keywords', Mongo::ASCENDING]])
24
+ t.ensure_index([['date', Mongo::DESCENDING]])
25
+ t.ensure_index([['address', Mongo::ASCENDING]])
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,91 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'net/imap'
4
+
5
+ namespace :ses_machine do
6
+ desc 'Check email with IMAP for new bounced and spam messages'
7
+ task :check_email_imap => :environment do
8
+
9
+ imap = Net::IMAP.new(SesMachine.email_server, SesMachine.email_port, SesMachine.email_use_ssl)
10
+ imap.login(SesMachine.email_account, SesMachine.email_password)
11
+ t = SesMachine.database['mails']
12
+
13
+ folders = SesMachine::Bounce::TYPES.keys.map {|i| i.to_s.classify}
14
+ folders.delete('EmailSent')
15
+ folders << 'MessageNotFound'
16
+ folders.each do |folder|
17
+ imap.create("SesMachine/#{folder}") unless imap.list('SesMachine/', folder)
18
+ end
19
+
20
+ SesMachine.email_imap_folders.each do |folder|
21
+ imap.select(folder)
22
+ imap.search(['FROM', 'email-bounces.amazonses.com', 'NOT', 'SEEN']).each do |message|
23
+ data = imap.fetch(message, ['UID', 'ENVELOPE', 'RFC822'])[0]
24
+ mail = Mail.read_from_string(data.attr['RFC822'])
25
+ message_id = data.attr['ENVELOPE']['in_reply_to']
26
+ if message_id.blank?
27
+ message_id = mail['X-Original-To'].to_s.split('@').first.to_s
28
+ else
29
+ message_id = message_id.split('@').first[1..-1].to_s
30
+ end
31
+ doc = t.find_one('message_id' => message_id)
32
+ if doc
33
+ email = doc['address'].first
34
+ bounce = {
35
+ :uid => data.attr['UID'],
36
+ :message_id => data.attr['ENVELOPE']['message_id'].split('@').first[1..-1],
37
+ :raw_source => data.attr['RFC822'],
38
+ :date => data.attr['ENVELOPE']['date'].to_time.utc
39
+ }
40
+ if mail['auto-submitted'].to_s == 'auto-replied'
41
+ imap.copy(message, 'SesMachine/AutoResponder')
42
+ bounce_type = SesMachine::Bounce::TYPES[:auto_responder]
43
+ SesMachine::Hooks.auto_responder_hook(email) if SesMachine::Hooks.respond_to?(:auto_responder_hook)
44
+ elsif mail.bounced?
45
+ if mail.retryable?
46
+ imap.copy(message, 'SesMachine/SoftBounce')
47
+ bounce_type = SesMachine::Bounce::TYPES[:soft_bounce]
48
+ SesMachine::Hooks.soft_bounce_hook(email) if SesMachine::Hooks.respond_to?(:soft_bounce_hook)
49
+ else
50
+ imap.copy(message, 'SesMachine/HardBounce')
51
+ bounce_type = SesMachine::Bounce::TYPES[:hard_bounce]
52
+ SesMachine::Hooks.hard_bounce_hook(email) if SesMachine::Hooks.respond_to?(:hard_bounce_hook)
53
+ end
54
+ bounce.merge!(:details => mail.diagnostic_code.to_s)
55
+ else
56
+ imap.copy(message, 'SesMachine/Unknown')
57
+ bounce_type = SesMachine::Bounce::TYPES[:unknown]
58
+ SesMachine::Hooks.unknown_hook(email) if SesMachine::Hooks.respond_to?(:unknown_hook)
59
+ end
60
+ t.update({'_id' => doc['_id']}, {'$set' => {'bounce' => bounce, 'bounce_type' => bounce_type}})
61
+ else
62
+ imap.copy(message, 'SesMachine/MessageNotFound')
63
+ end
64
+ end
65
+ end
66
+ SesMachine.email_imap_folders.each do |folder|
67
+ imap.select(folder)
68
+ imap.search(['FROM', 'complaints@email-abuse.amazonses.com', 'NOT', 'SEEN']).each do |message|
69
+ data = imap.fetch(message, ['UID', 'ENVELOPE', 'RFC822'])[0]
70
+ message_id = data.attr['RFC822'].split("\r\n").grep(/\AMessage-ID: (.+)\z/ix)[1][/<(.+)@/, 1].to_s
71
+ doc = t.find_one('message_id' => message_id)
72
+ if doc
73
+ email = doc['address'].first
74
+ bounce = {
75
+ :uid => data.attr['UID'],
76
+ :message_id => data.attr['ENVELOPE']['message_id'].split('@').first[1..-1],
77
+ :raw_source => data.attr['RFC822'],
78
+ :date => data.attr['ENVELOPE']['date'].to_time.utc
79
+ }
80
+ imap.copy(message, 'SesMachine/SpamComplaint')
81
+ SesMachine::Hooks.spam_complaint_hook(email) if SesMachine::Hooks.respond_to?(:spam_complaint_hook)
82
+ t.update({'_id' => doc['_id']}, {'$set' => {'bounce' => bounce, 'bounce_type' => SesMachine::Bounce::TYPES[:spam_complaint]}})
83
+ else
84
+ imap.copy(message, 'SesMachine/MessageNotFound')
85
+ end
86
+ end
87
+ end
88
+ imap.logout()
89
+ imap.disconnect()
90
+ end
91
+ end
File without changes
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/ses_machine/version', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ses_machine'
7
+ s.version = SesMachine::VERSION::STRING
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Kirill Nikitin']
10
+ s.email = ['locke23rus@gmail.com']
11
+ s.homepage = 'http://rubygems.org/gems/ses_machine'
12
+ s.summary = 'SES Machine'
13
+ s.description = 'SES Machine description'
14
+ s.license = 'MIT'
15
+
16
+ s.required_rubygems_version = '>= 1.3.6'
17
+
18
+ s.add_dependency 'mail', '>= 2.4.4'
19
+ s.add_dependency 'aws-ses', '>= 0.4.4'
20
+ s.add_dependency 'rubydkim', '>= 0.3.1'
21
+ s.add_dependency 'mongo', '>= 1.9.1'
22
+ s.add_dependency 'bson', '>= 1.9.1'
23
+ s.add_dependency 'bson_ext', '>= 1.9.1'
24
+ s.add_dependency 'will_paginate', '>= 3.0.4'
25
+
26
+ s.files = `git ls-files`.split("\n")
27
+ s.test_files = `git ls-files -- test/*`.split("\n")
28
+ s.require_path = 'lib'
29
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ConfigTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @config = SesMachine::Config.instance
9
+ @config.send(:remove_instance_variable, :@database) if @config.instance_variable_defined?(:@database)
10
+ end
11
+
12
+ def teardown
13
+ @config.send(:remove_instance_variable, :@database) if @config.instance_variable_defined?(:@database)
14
+ end
15
+
16
+ def test_load_config
17
+ assert_nil @config.database
18
+ filename = File.join(File.dirname(__FILE__), 'fixtures', 'config', 'ses_machine.yml')
19
+ @config.load(filename)
20
+ assert_not_nil @config.database
21
+ end
22
+
23
+ def test_set_database_with_invalid_database
24
+ assert_raise(SesMachine::Errors::InvalidDatabase) { @config.database = 'test' }
25
+ end
26
+
27
+ def test_load_config_from_hash
28
+ filename = File.join(File.dirname(__FILE__), 'fixtures', 'config', 'ses_machine.yml')
29
+ settings = YAML.load_file(filename)['test']
30
+ assert_nil @config.database
31
+ @config.from_hash(settings)
32
+ assert_equal 'ses_machine_config_test', @config.database.name
33
+ end
34
+ end
data/test/db_test.rb ADDED
@@ -0,0 +1,50 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class DBTest < ActiveSupport::TestCase
6
+
7
+ def setup
8
+ SesMachine.config.load(File.join(File.dirname(__FILE__), 'fixtures', 'config', 'ses_machine.yml'))
9
+ SesMachine.database['mails'].remove
10
+ SesMachine.database['daily_stats'].remove
11
+ SesMachine.database['monthly_stats'].remove
12
+ end
13
+
14
+ def teardown
15
+ SesMachine.database['mails'].remove
16
+ SesMachine.database['daily_stats'].remove
17
+ SesMachine.database['monthly_stats'].remove
18
+ end
19
+
20
+ # def test_get_keywords
21
+ # words = ['Q', 'qw', 'QWE', 'Й', 'йц', 'ЙЦУ', '1', '12', '123']
22
+ # assert_equal ['qwe', 'йцу', '123'], SesMachine::DB.get_keywords(words)
23
+ # end
24
+
25
+ def test_update_daily_stats
26
+ load_mails
27
+ response = SesMachine::DB.update_daily_stats
28
+ assert_equal 3, SesMachine.database['mails'].count
29
+ assert_equal 2, SesMachine.database['daily_stats'].count
30
+ assert_equal 1, response['ok']
31
+ end
32
+
33
+ def test_update_monthly_stats
34
+ load_mails
35
+ SesMachine::DB.update_daily_stats
36
+ assert_equal 0, SesMachine.database['monthly_stats'].count
37
+ response = SesMachine::DB.update_monthly_stats
38
+ assert_equal 2, SesMachine.database['monthly_stats'].count
39
+ assert_equal 1, response['ok']
40
+ end
41
+
42
+ private
43
+
44
+ def load_mails
45
+ SesMachine.database['mails'].insert('bounce_type' => SesMachine::Bounce::TYPES[:mail_sent], 'date' => 2.month.ago.to_time.utc)
46
+ SesMachine.database['mails'].insert('bounce_type' => SesMachine::Bounce::TYPES[:unknown], 'date' => Time.now.utc)
47
+ SesMachine.database['mails'].insert('bounce_type' => SesMachine::Bounce::TYPES[:hard_bounce], 'date' => Time.now.utc)
48
+ end
49
+
50
+ end
@@ -0,0 +1,11 @@
1
+ defaults: &defaults
2
+ database:
3
+ name: ses_machine_test
4
+ host: localhost
5
+ port: 27017
6
+
7
+
8
+ test:
9
+ <<: *defaults
10
+ database:
11
+ name: ses_machine_config_test
@@ -0,0 +1,17 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class RoutingTest < ActionController::TestCase
6
+ def setup
7
+ ActionController::Routing::Routes.draw do |map|
8
+ map.ses_machine
9
+ end
10
+ end
11
+
12
+ test 'map index ses machine' do
13
+ assert_recognizes({:controller => 'ses_machine', :action => 'index'}, {:path => 'ses_machine', :method => :get})
14
+ assert_recognizes({:controller => 'ses_machine', :action => 'activity'}, {:path => 'ses_machine/activity', :method => :get})
15
+ assert_recognizes({:controller => 'ses_machine', :action => 'show_message', :id => '1'}, {:path => 'ses_machine/message/1', :method => :get})
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class SesMachineControllerTest < ActionController::TestCase
6
+ def setup
7
+ SesMachine.config.load(File.join(File.dirname(__FILE__), 'fixtures', 'config', 'ses_machine.yml'))
8
+ SesMachine.database['mails'].remove
9
+ SesMachine.database['mails'].insert('_id' => BSON::ObjectId('4d95f4ebf023ca49fa000001'),
10
+ 'date' => Time.now.utc,
11
+ 'address' => ['test@example.com'],
12
+ 'subject' => 'Test mail',
13
+ 'raw_source' => 'Test')
14
+ ActionController::Routing::Routes.draw { |map| map.ses_machine }
15
+ @controller = SesMachineController.new
16
+ end
17
+
18
+ def teardown
19
+ SesMachine.database['mails'].remove
20
+ end
21
+
22
+ def test_index
23
+ get :index
24
+ assert_response :success
25
+ assert_not_nil assigns(:count_mails_sent)
26
+ assert_not_nil assigns(:count_mails_bounced)
27
+ assert_not_nil assigns(:monthly_stats)
28
+ assert_not_nil assigns(:date)
29
+ end
30
+
31
+ def test_activity
32
+ get :activity
33
+ assert_response :success
34
+ assert_not_nil assigns(:messages)
35
+ assert_not_nil assigns(:bounce_types)
36
+ end
37
+
38
+ def test_show_message
39
+ get :show_message, :id => '4d95f4ebf023ca49fa000001'
40
+ assert_response :success
41
+ assert_not_nil assigns(:mail)
42
+ end
43
+
44
+ def test_show_message_without_id
45
+ get :show_message, :id => '12345'
46
+ assert_redirected_to ses_machine_path
47
+ assert_not_nil flash[:error]
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'rails_generator'
5
+ require 'rails_generator/scripts/generate'
6
+
7
+ generators_path = File.join(File.dirname(__FILE__), '..', 'generators')
8
+ Rails::Generator::Base.append_sources(Rails::Generator::PathSource.new(:plugin, generators_path))
9
+
10
+ class SesMachineGeneratorTest < Test::Unit::TestCase
11
+
12
+ def setup
13
+ FileUtils.mkdir_p(fake_rails_root)
14
+ end
15
+
16
+ def teardown
17
+ FileUtils.rm_r(fake_rails_root)
18
+ end
19
+
20
+ def test_generates_correct_files
21
+ Rails::Generator::Scripts::Generate.new.run(['ses_machine'], :destination => fake_rails_root, :quiet => true)
22
+ assert File.exist?(File.join(fake_rails_root, 'config', 'ses_machine.yml'))
23
+ assert File.exist?(File.join(fake_rails_root, 'config', 'initializers', 'ses_machine.rb'))
24
+ assert File.exist?(File.join(fake_rails_root, 'config', 'initializers', 'ses_machine_hooks.rb'))
25
+ assert File.exist?(File.join(fake_rails_root, 'public', 'stylesheets', 'ses_machine.css'))
26
+ end
27
+
28
+ private
29
+
30
+ def fake_rails_root
31
+ File.join(File.dirname(__FILE__), 'rails_root')
32
+ end
33
+
34
+ def file_list
35
+ Dir.glob(File.join(fake_rails_root, '*'))
36
+ end
37
+
38
+ end
@@ -0,0 +1,10 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class SesMachineTest < ActiveSupport::TestCase
6
+ # Replace this with your real tests.
7
+ test "the truth" do
8
+ assert true
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ ENV['RAILS_ENV'] = 'test'
4
+ ENV['RAILS_ROOT'] = File.join(File.dirname(__FILE__), 'fixtures')
5
+
6
+ require 'test/unit'
7
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ses_machine')
data/uninstall.rb ADDED
@@ -0,0 +1,3 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ # Uninstall hook code here