MattHall-campaign_monitor 1.3.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +52 -0
- data/Rakefile +31 -0
- data/TODO +6 -0
- data/campaign_monitor.gemspec +53 -0
- data/init.rb +1 -0
- data/install.rb +0 -0
- data/lib/campaign_monitor/base.rb +55 -0
- data/lib/campaign_monitor/campaign.rb +240 -0
- data/lib/campaign_monitor/client.rb +228 -0
- data/lib/campaign_monitor/helpers.rb +27 -0
- data/lib/campaign_monitor/list.rb +211 -0
- data/lib/campaign_monitor/misc.rb +46 -0
- data/lib/campaign_monitor/result.rb +31 -0
- data/lib/campaign_monitor/subscriber.rb +43 -0
- data/lib/campaign_monitor.rb +270 -0
- data/support/class_enhancements.rb +35 -0
- data/support/faster-xml-simple/lib/faster_xml_simple.rb +187 -0
- data/support/faster-xml-simple/test/regression_test.rb +47 -0
- data/support/faster-xml-simple/test/test_helper.rb +17 -0
- data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +46 -0
- data/test/campaign_monitor_test.rb +90 -0
- data/test/campaign_test.rb +110 -0
- data/test/client_test.rb +129 -0
- data/test/list_test.rb +115 -0
- data/test/test_helper.rb +27 -0
- metadata +97 -0
@@ -0,0 +1,187 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2006 Michael Koziarski
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in the
|
6
|
+
# Software without restriction, including without limitation the rights to use,
|
7
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
8
|
+
# Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
18
|
+
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
19
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'rubygems'
|
22
|
+
require 'xml/libxml'
|
23
|
+
|
24
|
+
class FasterXmlSimple
|
25
|
+
Version = '0.5.0'
|
26
|
+
class << self
|
27
|
+
# Take an string containing XML, and returns a hash representing that
|
28
|
+
# XML document. For example:
|
29
|
+
#
|
30
|
+
# FasterXmlSimple.xml_in("<root><something>1</something></root>")
|
31
|
+
# {"root"=>{"something"=>{"__content__"=>"1"}}}
|
32
|
+
#
|
33
|
+
# Faster XML Simple is designed to be a drop in replacement for the xml_in
|
34
|
+
# functionality of http://xml-simple.rubyforge.org
|
35
|
+
#
|
36
|
+
# The following options are supported:
|
37
|
+
#
|
38
|
+
# * <tt>contentkey</tt>: The key to use for the content of text elements,
|
39
|
+
# defaults to '\_\_content__'
|
40
|
+
# * <tt>forcearray</tt>: The list of elements which should always be returned
|
41
|
+
# as arrays. Under normal circumstances single element arrays are inlined.
|
42
|
+
# * <tt>suppressempty</tt>: The value to return for empty elements, pass +true+
|
43
|
+
# to remove empty elements entirely.
|
44
|
+
# * <tt>keeproot</tt>: By default the hash returned has a single key with the
|
45
|
+
# name of the root element. If the name of the root element isn't
|
46
|
+
# interesting to you, pass +false+.
|
47
|
+
# * <tt>forcecontent</tt>: By default a text element with no attributes, will
|
48
|
+
# be collapsed to just a string instead of a hash with a single key.
|
49
|
+
# Pass +true+ to prevent this.
|
50
|
+
#
|
51
|
+
#
|
52
|
+
def xml_in(string, options={})
|
53
|
+
new(string, options).out
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(string, options) #:nodoc:
|
58
|
+
@doc = parse(string)
|
59
|
+
@options = default_options.merge options
|
60
|
+
end
|
61
|
+
|
62
|
+
def out #:nodoc:
|
63
|
+
if @options['keeproot']
|
64
|
+
{@doc.root.name => collapse(@doc.root)}
|
65
|
+
else
|
66
|
+
collapse(@doc.root)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def default_options
|
72
|
+
{'contentkey' => '__content__', 'forcearray' => [], 'keeproot'=>true}
|
73
|
+
end
|
74
|
+
|
75
|
+
def collapse(element)
|
76
|
+
result = hash_of_attributes(element)
|
77
|
+
if text_node? element
|
78
|
+
text = collapse_text(element)
|
79
|
+
result[content_key] = text if text =~ /\S/
|
80
|
+
elsif element.children?
|
81
|
+
element.inject(result) do |hash, child|
|
82
|
+
unless child.text?
|
83
|
+
child_result = collapse(child)
|
84
|
+
(hash[child.name] ||= []) << child_result
|
85
|
+
end
|
86
|
+
hash
|
87
|
+
end
|
88
|
+
end
|
89
|
+
if result.empty?
|
90
|
+
return empty_element
|
91
|
+
end
|
92
|
+
# Compact them to ensure it complies with the user's requests
|
93
|
+
inline_single_element_arrays(result)
|
94
|
+
remove_empty_elements(result) if suppress_empty?
|
95
|
+
if content_only?(result) && !force_content?
|
96
|
+
result[content_key]
|
97
|
+
else
|
98
|
+
result
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def content_only?(result)
|
103
|
+
result.keys == [content_key]
|
104
|
+
end
|
105
|
+
|
106
|
+
def content_key
|
107
|
+
@options['contentkey']
|
108
|
+
end
|
109
|
+
|
110
|
+
def force_array?(key_name)
|
111
|
+
Array(@options['forcearray']).include?(key_name)
|
112
|
+
end
|
113
|
+
|
114
|
+
def inline_single_element_arrays(result)
|
115
|
+
result.each do |key, value|
|
116
|
+
if value.size == 1 && value.is_a?(Array) && !force_array?(key)
|
117
|
+
result[key] = value.first
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def remove_empty_elements(result)
|
123
|
+
result.each do |key, value|
|
124
|
+
if value == empty_element
|
125
|
+
result.delete key
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def suppress_empty?
|
131
|
+
@options['suppressempty'] == true
|
132
|
+
end
|
133
|
+
|
134
|
+
def empty_element
|
135
|
+
if !@options.has_key? 'suppressempty'
|
136
|
+
{}
|
137
|
+
else
|
138
|
+
@options['suppressempty']
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# removes the content if it's nothing but blanks, prevents
|
143
|
+
# the hash being polluted with lots of content like "\n\t\t\t"
|
144
|
+
def suppress_empty_content(result)
|
145
|
+
result.delete content_key if result[content_key] !~ /\S/
|
146
|
+
end
|
147
|
+
|
148
|
+
def force_content?
|
149
|
+
@options['forcecontent']
|
150
|
+
end
|
151
|
+
|
152
|
+
# a text node is one with 1 or more child nodes which are
|
153
|
+
# text nodes, and no non-text children, there's no sensible
|
154
|
+
# way to support nodes which are text and markup like:
|
155
|
+
# <p>Something <b>Bold</b> </p>
|
156
|
+
def text_node?(element)
|
157
|
+
!element.text? && element.all? {|c| c.text?}
|
158
|
+
end
|
159
|
+
|
160
|
+
# takes a text node, and collapses it into a string
|
161
|
+
def collapse_text(element)
|
162
|
+
element.map {|c| c.content } * ''
|
163
|
+
end
|
164
|
+
|
165
|
+
def hash_of_attributes(element)
|
166
|
+
result = {}
|
167
|
+
element.each_attr do |attribute|
|
168
|
+
name = attribute.name
|
169
|
+
name = [attribute.ns, attribute.name].join(':') if attribute.ns?
|
170
|
+
result[name] = attribute.value
|
171
|
+
end
|
172
|
+
result
|
173
|
+
end
|
174
|
+
|
175
|
+
def parse(string)
|
176
|
+
if string == ''
|
177
|
+
string = ' '
|
178
|
+
end
|
179
|
+
XML::Parser.string(string).parse
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class XmlSimple # :nodoc:
|
184
|
+
def self.xml_in(*args)
|
185
|
+
FasterXmlSimple.xml_in *args
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class RegressionTest < FasterXSTest #:nodoc: all
|
4
|
+
def test_content_nil_regressions
|
5
|
+
expected = {"asdf"=>{"jklsemicolon"=>{}}}
|
6
|
+
assert_equal expected, FasterXmlSimple.xml_in("<asdf><jklsemicolon /></asdf>")
|
7
|
+
assert_equal expected, FasterXmlSimple.xml_in("<asdf><jklsemicolon /></asdf>", 'forcearray'=>['asdf'])
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_s3_regression
|
11
|
+
str = File.read("test/fixtures/test-7.xml")
|
12
|
+
assert_nil FasterXmlSimple.xml_in(str)["AccessControlPolicy"]["AccessControlList"]["__content__"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_xml_simple_transparency
|
16
|
+
assert_equal XmlSimple.xml_in("<asdf />"), FasterXmlSimple.xml_in("<asdf />")
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_suppress_empty_variations
|
20
|
+
str = "<asdf><fdsa /></asdf>"
|
21
|
+
|
22
|
+
assert_equal Hash.new, FasterXmlSimple.xml_in(str)["asdf"]["fdsa"]
|
23
|
+
assert_nil FasterXmlSimple.xml_in(str, 'suppressempty'=>nil)["asdf"]["fdsa"]
|
24
|
+
assert_equal '', FasterXmlSimple.xml_in(str, 'suppressempty'=>'')["asdf"]["fdsa"]
|
25
|
+
assert !FasterXmlSimple.xml_in(str, 'suppressempty'=>true)["asdf"].has_key?("fdsa")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_empty_string_doesnt_crash
|
29
|
+
assert_raise(XML::Parser::ParseError) do
|
30
|
+
silence_stderr do
|
31
|
+
FasterXmlSimple.xml_in('')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_keeproot_false
|
37
|
+
str = "<asdf><fdsa>1</fdsa></asdf>"
|
38
|
+
expected = {"fdsa"=>"1"}
|
39
|
+
assert_equal expected, FasterXmlSimple.xml_in(str, 'keeproot'=>false)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_keeproot_false_with_force_content
|
43
|
+
str = "<asdf><fdsa>1</fdsa></asdf>"
|
44
|
+
expected = {"fdsa"=>{"__content__"=>"1"}}
|
45
|
+
assert_equal expected, FasterXmlSimple.xml_in(str, 'keeproot'=>false, 'forcecontent'=>true)
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'faster_xml_simple'
|
4
|
+
|
5
|
+
class FasterXSTest < Test::Unit::TestCase
|
6
|
+
def default_test
|
7
|
+
end
|
8
|
+
|
9
|
+
def silence_stderr
|
10
|
+
str = STDERR.dup
|
11
|
+
STDERR.reopen("/dev/null")
|
12
|
+
STDERR.sync=true
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
STDERR.reopen(str)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
class XmlSimpleComparisonTest < FasterXSTest
|
5
|
+
|
6
|
+
# Define test methods
|
7
|
+
|
8
|
+
Dir["test/fixtures/test-*.xml"].each do |file_name|
|
9
|
+
xml_file_name = file_name
|
10
|
+
method_name = File.basename(file_name, ".xml").gsub('-', '_')
|
11
|
+
yml_file_name = file_name.gsub('xml', 'yml')
|
12
|
+
rails_yml_file_name = file_name.gsub('xml', 'rails.yml')
|
13
|
+
class_eval <<-EOV, __FILE__, __LINE__
|
14
|
+
def #{method_name}
|
15
|
+
assert_equal YAML.load(File.read('#{yml_file_name}')),
|
16
|
+
FasterXmlSimple.xml_in(File.read('#{xml_file_name}'), default_options )
|
17
|
+
end
|
18
|
+
|
19
|
+
def #{method_name}_rails
|
20
|
+
assert_equal YAML.load(File.read('#{rails_yml_file_name}')),
|
21
|
+
FasterXmlSimple.xml_in(File.read('#{xml_file_name}'), rails_options)
|
22
|
+
end
|
23
|
+
EOV
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_options
|
27
|
+
{
|
28
|
+
'keeproot' => true,
|
29
|
+
'contentkey' => '__content__',
|
30
|
+
'forcecontent' => true,
|
31
|
+
'suppressempty' => nil,
|
32
|
+
'forcearray' => ['something-else']
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def rails_options
|
37
|
+
{
|
38
|
+
'forcearray' => false,
|
39
|
+
'forcecontent' => true,
|
40
|
+
'keeproot' => true,
|
41
|
+
'contentkey' => '__content__'
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'lib/campaign_monitor'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'test/test_helper'
|
5
|
+
|
6
|
+
CLIENT_NAME = 'Spacely Space Sprockets'
|
7
|
+
LIST_NAME = 'List #1'
|
8
|
+
|
9
|
+
class CampaignMonitorTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@cm = CampaignMonitor.new(ENV["API_KEY"])
|
13
|
+
# find an existing client
|
14
|
+
@client=find_test_client
|
15
|
+
assert_not_nil @client, "Please create a '#{CLIENT_NAME}' (company name) client so tests can run."
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# def test_create_and_delete_client
|
20
|
+
# before=@cm.clients.size
|
21
|
+
# response = @cm.Client_Create(build_new_client)
|
22
|
+
# puts response.inspect
|
23
|
+
# assert_equal before+1, @cm.clients.size
|
24
|
+
# @client_id=response["__content__"]
|
25
|
+
# reponse = @cm.Client_Delete("ClientID" => @client_id)
|
26
|
+
# assert_equal before, @cm.clients.size
|
27
|
+
# end
|
28
|
+
|
29
|
+
def test_find_existing_client_by_name
|
30
|
+
clients = @cm.clients
|
31
|
+
assert clients.size > 0
|
32
|
+
|
33
|
+
assert clients.map {|c| c.name}.include?(CLIENT_NAME), "could not find client named: #{CLIENT_NAME}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_invalid_key
|
37
|
+
@cm=CampaignMonitor.new("12345")
|
38
|
+
assert_raises (CampaignMonitor::InvalidAPIKey) { @cm.clients }
|
39
|
+
end
|
40
|
+
|
41
|
+
# we should not get confused here
|
42
|
+
def test_can_access_two_accounts_at_once
|
43
|
+
@cm=CampaignMonitor.new("12345")
|
44
|
+
@cm2=CampaignMonitor.new("abcdef")
|
45
|
+
@client=@cm.new_client
|
46
|
+
@client2=@cm.new_client
|
47
|
+
assert_equal "12345", @client.cm_client.api_key
|
48
|
+
assert_equal "abcdef", @client2.cm_client.api_key
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_timezones
|
52
|
+
assert_equal 90, @cm.timezones.length
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_countries
|
56
|
+
countries=@cm.countries
|
57
|
+
assert_equal 246, countries.length
|
58
|
+
assert countries.include?("United States of America")
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# campaigns
|
63
|
+
|
64
|
+
# def test_campaigns
|
65
|
+
# client = find_test_client
|
66
|
+
# assert client.campaigns.size > 0, "should have one campaign"
|
67
|
+
# end
|
68
|
+
|
69
|
+
|
70
|
+
protected
|
71
|
+
def build_new_client(options={})
|
72
|
+
{"CompanyName" => "Spacely Space Sprockets", "ContactName" => "George Jetson",
|
73
|
+
"EmailAddress" => "george@sss.com", "Country" => "United States of America",
|
74
|
+
"TimeZone" => "(GMT-05:00) Indiana (East)"
|
75
|
+
}.merge(options)
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def assert_success(result)
|
80
|
+
assert result.succeeded?, result.message
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_test_client(clients=@cm.clients)
|
84
|
+
clients.detect { |c| c.name == CLIENT_NAME }
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_test_list(lists=find_test_client.lists)
|
88
|
+
lists.detect { |l| l.name == LIST_NAME }
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'lib/campaign_monitor'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'test/test_helper'
|
5
|
+
|
6
|
+
CLIENT_NAME = 'Spacely Space Sprockets'
|
7
|
+
CLIENT_CONTACT_NAME = 'George Jetson'
|
8
|
+
LIST_NAME = 'List #1'
|
9
|
+
|
10
|
+
class CampaignMonitorTest < Test::Unit::TestCase
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@cm = CampaignMonitor.new(ENV["API_KEY"])
|
14
|
+
# find an existing client and make sure we know it's values
|
15
|
+
@client=find_test_client(@cm.clients)
|
16
|
+
assert_not_nil @client, "Please create a '#{CLIENT_NAME}' (company name) client so tests can run."
|
17
|
+
|
18
|
+
@campaign=@client.campaigns.detect { |x| x["Subject"] == "Big Deal" }
|
19
|
+
assert_not_nil @campaign, "Please create a campaign named 'Big Deal' so tests can run."
|
20
|
+
|
21
|
+
# delete all existing lists
|
22
|
+
@client.lists.each { |l| l.Delete }
|
23
|
+
@list = @client.lists.build.defaults
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_finds_named_campaign
|
30
|
+
assert_equal 1, @campaign["TotalRecipients"]
|
31
|
+
assert_equal 1, @campaign.lists.size
|
32
|
+
assert_equal Hash.new, @campaign.lists.first["Title"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_bracket_lookup_for_nonexistant
|
36
|
+
@campaign=CampaignMonitor::Campaign[12345]
|
37
|
+
assert_not_nil @campaign
|
38
|
+
assert_equal 12345, @campaign.id
|
39
|
+
assert_raises( CampaignMonitor::ApiError ) { @campaign.lists }
|
40
|
+
assert_raises( CampaignMonitor::ApiError ) { @campaign.GetSummary }
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_bracket_lookup_for_existing
|
44
|
+
camp=CampaignMonitor::Campaign[@campaign.id]
|
45
|
+
assert_not_nil camp
|
46
|
+
camp.lists
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_summary_interface
|
50
|
+
# old
|
51
|
+
assert_equal 1, @campaign.number_recipients
|
52
|
+
assert_equal 0, @campaign.number_opened
|
53
|
+
assert_equal 0, @campaign.number_clicks
|
54
|
+
assert_equal 0, @campaign.number_unsubscribed
|
55
|
+
assert_equal 0, @campaign.number_bounced
|
56
|
+
# new
|
57
|
+
assert_equal 1, @campaign.summary["Recipients"]
|
58
|
+
assert_equal 0, @campaign.summary["TotalOpened"]
|
59
|
+
assert_equal 0, @campaign.summary["Clicks"]
|
60
|
+
assert_equal 0, @campaign.summary["Unsubscribed"]
|
61
|
+
assert_equal 0, @campaign.summary["Bounced"]
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_creating_a_campaign
|
65
|
+
return
|
66
|
+
@campaign=@client.new_campaign
|
67
|
+
# create two lists
|
68
|
+
@beef=@client.lists.build.defaults
|
69
|
+
@beef["Title"]="Beef"
|
70
|
+
@beef.Create
|
71
|
+
assert_success @beef.result
|
72
|
+
@chicken=@client.lists.build.defaults
|
73
|
+
@chicken["Title"]="Chicken"
|
74
|
+
@chicken.Create
|
75
|
+
assert_success @chicken.result
|
76
|
+
|
77
|
+
@campaign.lists << @beef
|
78
|
+
@campaign.lists << @chicken
|
79
|
+
@campaign["CampaignName"]="Noodles #{secure_digest(Time.now.to_s)}"
|
80
|
+
@campaign["CampaignSubject"]="Noodly #{secure_digest(Time.now.to_s)}"
|
81
|
+
@campaign["FromName"] = "George Bush"
|
82
|
+
@campaign["FromEmail"] = "george@aol.com"
|
83
|
+
@campaign["ReplyTo"] = "george@aol.com"
|
84
|
+
@campaign["HtmlUrl"] = "http://www.google.com/robots.txt"
|
85
|
+
@campaign["TextUrl"] = "http://www.google.com/robots.txt"
|
86
|
+
@campaign.Create
|
87
|
+
assert_success @campaign.result
|
88
|
+
assert_not_nil @campaign.id
|
89
|
+
assert_equal 32, @campaign.id.length
|
90
|
+
# test GetLists
|
91
|
+
@campaign.instance_variable_set("@lists",nil)
|
92
|
+
assert_equal 2, @campaign.lists.size
|
93
|
+
# test sending
|
94
|
+
@campaign.Send("ConfirmationEmail" => "george@aol.com", "SendDate" => "Immediately")
|
95
|
+
assert_success @campaign.result
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_GetSummary
|
99
|
+
@campaign.GetSummary
|
100
|
+
assert @campaign.result.success?
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def find_test_client(clients)
|
107
|
+
clients.detect { |c| c.name == CLIENT_NAME }
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'lib/campaign_monitor'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'test/test_helper'
|
5
|
+
|
6
|
+
CLIENT_NAME = 'Spacely Space Sprockets'
|
7
|
+
CLIENT_CONTACT_NAME = 'George Jetson'
|
8
|
+
LIST_NAME = 'List #1'
|
9
|
+
|
10
|
+
class CampaignMonitorTest < Test::Unit::TestCase
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@cm = CampaignMonitor.new(ENV["API_KEY"])
|
14
|
+
# find an existing client and make sure we know it's values
|
15
|
+
@client=find_test_client
|
16
|
+
assert_not_nil @client, "Please create a '#{CLIENT_NAME}' (company name) client so tests can run."
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
# revert it back
|
22
|
+
@client["ContactName"]="George Jetson"
|
23
|
+
@client["EmailAddress"]="george@sss.com"
|
24
|
+
|
25
|
+
@client["Username"]=""
|
26
|
+
@client["Password"]=""
|
27
|
+
@client["AccessLevel"]=0
|
28
|
+
|
29
|
+
assert @client.update
|
30
|
+
assert_success @client.result
|
31
|
+
end
|
32
|
+
|
33
|
+
# def test_create_and_delete_client
|
34
|
+
# before=@cm.clients.size
|
35
|
+
# client=CampaignMonitor::Client.new(build_new_client)
|
36
|
+
# client.Create
|
37
|
+
# assert_success client.result
|
38
|
+
# assert_equal before+1, @cm.clients.size
|
39
|
+
# client.Delete
|
40
|
+
# assert_success client.result
|
41
|
+
# assert_equal before, @cm.clients.size
|
42
|
+
# end
|
43
|
+
|
44
|
+
def test_find_existing_client_by_name
|
45
|
+
clients = @cm.clients
|
46
|
+
assert clients.size > 0
|
47
|
+
|
48
|
+
assert clients.map {|c| c.name}.include?(CLIENT_NAME), "could not find client named: #{CLIENT_NAME}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_client_attributes
|
52
|
+
assert_equal CLIENT_NAME, @client["CompanyName"]
|
53
|
+
assert_nil @client["ContactName"]
|
54
|
+
assert @client.GetDetail
|
55
|
+
assert_not_empty @client["ContactName"]
|
56
|
+
assert_not_empty @client["EmailAddress"]
|
57
|
+
assert_not_empty @client["Country"]
|
58
|
+
assert_not_empty @client["Timezone"]
|
59
|
+
|
60
|
+
assert_nil @client["Username"]
|
61
|
+
assert_nil @client["Password"]
|
62
|
+
assert_equal 0, @client["AccessLevel"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_update_client_basics
|
66
|
+
# only update the name
|
67
|
+
@client["ContactName"]="Bob Watson"
|
68
|
+
assert @client.UpdateBasics
|
69
|
+
assert_success @client.result
|
70
|
+
client=@cm.clients.detect {|x| x.name==CLIENT_NAME}
|
71
|
+
client.GetDetail
|
72
|
+
assert_equal "Bob Watson", client["ContactName"]
|
73
|
+
# make sure e-mail has remained unchanged
|
74
|
+
assert_equal "george@sss.com", client["EmailAddress"]
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_update_access_and_billing
|
78
|
+
@client["Username"]="login"
|
79
|
+
@client["Password"]="secret"
|
80
|
+
@client["AccessLevel"]=63
|
81
|
+
@client["BillingType"]="ClientPaysAtStandardRate"
|
82
|
+
@client["Currency"]="USD"
|
83
|
+
@client.UpdateAccessAndBilling
|
84
|
+
assert_success @client.result
|
85
|
+
# load it up again
|
86
|
+
client=@cm.clients.detect {|x| x.name==CLIENT_NAME}
|
87
|
+
client.GetDetail
|
88
|
+
assert_equal "login", client["Username"]
|
89
|
+
assert_equal "secret", client["Password"]
|
90
|
+
assert_equal "ClientPaysAtStandardRate", client["BillingType"]
|
91
|
+
assert_equal 63, client["AccessLevel"]
|
92
|
+
assert_equal "USD", client["Currency"]
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_update_both
|
96
|
+
@client["ContactName"]="Bob Watson"
|
97
|
+
@client["Username"]="login"
|
98
|
+
@client["Password"]="secret"
|
99
|
+
@client["AccessLevel"]=63
|
100
|
+
@client["BillingType"]="ClientPaysAtStandardRate"
|
101
|
+
@client["Currency"]="USD"
|
102
|
+
@client.update
|
103
|
+
assert_success @client.result
|
104
|
+
assert_equal "login", @client["Username"]
|
105
|
+
assert_equal "Bob Watson", @client["ContactName"]
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
protected
|
110
|
+
def build_new_client(options={})
|
111
|
+
{"CompanyName" => "Lick More Enterprises", "ContactName" => "George Jetson",
|
112
|
+
"EmailAddress" => "george@jetson.com", "Country" => "United States of America",
|
113
|
+
"TimeZone" => "(GMT-05:00) Indiana (East)"
|
114
|
+
}.merge(options)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def assert_success(result)
|
119
|
+
assert result.succeeded?, result.message
|
120
|
+
end
|
121
|
+
|
122
|
+
def find_test_client(clients=@cm.clients)
|
123
|
+
clients.detect { |c| c.name == CLIENT_NAME }
|
124
|
+
end
|
125
|
+
|
126
|
+
def find_test_list(lists=find_test_client.lists)
|
127
|
+
lists.detect { |l| l.name == LIST_NAME }
|
128
|
+
end
|
129
|
+
end
|