nightcrawler_swift 0.4.0 → 0.5.0

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +4 -1
  4. data/Changelog.md +12 -2
  5. data/Gemfile.lock +11 -1
  6. data/README.md +112 -7
  7. data/bin/nswift +1 -1
  8. data/lib/nightcrawler_swift/cli/commands/url_for.rb +9 -0
  9. data/lib/nightcrawler_swift/cli/formatters/basic.rb +40 -0
  10. data/lib/nightcrawler_swift/cli/opt_parser.rb +81 -0
  11. data/lib/nightcrawler_swift/cli/runner.rb +127 -0
  12. data/lib/nightcrawler_swift/cli.rb +16 -173
  13. data/lib/nightcrawler_swift/command.rb +5 -14
  14. data/lib/nightcrawler_swift/commands/delete.rb +4 -6
  15. data/lib/nightcrawler_swift/commands/download.rb +4 -6
  16. data/lib/nightcrawler_swift/commands/list.rb +0 -6
  17. data/lib/nightcrawler_swift/commands/upload.rb +1 -4
  18. data/lib/nightcrawler_swift/connection.rb +15 -15
  19. data/lib/nightcrawler_swift/exceptions.rb +2 -1
  20. data/lib/nightcrawler_swift/gateway.rb +68 -0
  21. data/lib/nightcrawler_swift/version.rb +1 -1
  22. data/lib/nightcrawler_swift.rb +7 -2
  23. data/nightcrawler_swift.gemspec +1 -0
  24. data/spec/lib/nightcrawler_swift/cli/commands/url_for_spec.rb +34 -0
  25. data/spec/lib/nightcrawler_swift/cli/formatters/basic_spec.rb +117 -0
  26. data/spec/lib/nightcrawler_swift/cli/opt_parser_spec.rb +135 -0
  27. data/spec/lib/nightcrawler_swift/{cli_spec.rb → cli/runner_spec.rb} +133 -136
  28. data/spec/lib/nightcrawler_swift/command_spec.rb +17 -32
  29. data/spec/lib/nightcrawler_swift/commands/delete_spec.rb +8 -29
  30. data/spec/lib/nightcrawler_swift/commands/download_spec.rb +8 -29
  31. data/spec/lib/nightcrawler_swift/commands/list_spec.rb +14 -44
  32. data/spec/lib/nightcrawler_swift/commands/upload_spec.rb +1 -8
  33. data/spec/lib/nightcrawler_swift/connection_spec.rb +26 -9
  34. data/spec/lib/nightcrawler_swift/gateway_spec.rb +139 -0
  35. data/spec/lib/nightcrawler_swift_spec.rb +15 -2
  36. data/spec/spec_helper.rb +3 -0
  37. metadata +31 -4
@@ -1,10 +1,16 @@
1
1
  require "optparse"
2
2
  require "json"
3
3
 
4
+ require "nightcrawler_swift/cli/opt_parser"
5
+ require "nightcrawler_swift/cli/runner"
6
+ require "nightcrawler_swift/cli/formatters/basic"
7
+ require "nightcrawler_swift/cli/commands/url_for"
8
+
4
9
  module NightcrawlerSwift
5
- class CLI
10
+ module CLI
6
11
  CONFIG_FILE = ".nswiftrc"
7
12
  CACHE_FILE = ".nswift_cache"
13
+
8
14
  COMMANDS = {
9
15
  "list" => {
10
16
  description: "Lists all files of the bucket/container. Ex: nswift list",
@@ -12,189 +18,27 @@ module NightcrawlerSwift
12
18
  },
13
19
 
14
20
  "download" => {
15
- description: "Downloads a file by path. Format: nswift download <swift path> Ex: nswift download assets/robots.txt > my-robots.txt",
21
+ description: "Downloads a file by path. Format: nswift download <swift_path> Ex: nswift download assets/robots.txt > my-robots.txt",
16
22
  command: NightcrawlerSwift::Download
17
23
  },
18
24
 
19
25
  "upload" => {
20
- description: "Uploads some file. Format: nswift upload <real path> <swift path> Ex: nswift upload assets/robots.txt robots.txt",
26
+ description: "Uploads some file. Format: nswift upload <real_path> <swift_path> Ex: nswift upload assets/robots.txt robots.txt",
21
27
  command: NightcrawlerSwift::Upload
22
28
  },
23
29
 
24
30
  "delete" => {
25
- description: "Deletes a file by path. Format: nswift delete <swift path> Ex: nswift delete robots.txt",
31
+ description: "Deletes a file by path. Format: nswift delete <swift_path> Ex: nswift delete robots.txt",
26
32
  command: NightcrawlerSwift::Delete
33
+ },
34
+
35
+ "url-for" => {
36
+ description: "Returns the public url of an object. Format: nswift url-for <swift_path> Ex: nswift url-for robots.txt",
37
+ command: NightcrawlerSwift::CLI::UrlFor
27
38
  }
28
39
  }
29
40
 
30
- attr_reader :opt_parser, :options
31
- attr_accessor :argv
32
-
33
- def initialize argv
34
- @argv = argv
35
- STDOUT.sync = true
36
- NightcrawlerSwift.logger.formatter = lambda {|severity, datetime, progname, msg| "#{msg}\n"}
37
- end
38
-
39
- def run
40
- configure_default_options
41
- parse_parameters
42
- @command_name = argv.shift
43
- validate_command_and_options
44
- execute_command if @command_name
45
- end
46
-
47
- protected
48
- def command_list command
49
- array = command.new.execute
50
- array.each {|hash| log hash["name"]}
51
- end
52
-
53
- def command_download command
54
- filepath = argv.first
55
- log command.new.execute(filepath)
56
- end
57
-
58
- def command_upload command
59
- realpath = argv.shift
60
- swiftpath = argv.shift
61
-
62
- uploaded = command.new.execute swiftpath, File.open(File.expand_path(realpath), "r")
63
- log(uploaded ? "success" : "failure")
64
- end
65
-
66
- def command_delete command
67
- filepath = argv.first
68
- deleted = command.new.execute(filepath).to_json
69
- log(deleted ? "success" : "failure")
70
- end
71
-
72
- def user_home_dir
73
- Dir.home
74
- end
75
-
76
- def log string
77
- NightcrawlerSwift.logger.info string
78
- end
79
-
80
- private
81
- def configure_default_options
82
- @options = OpenStruct.new
83
- @options.configured = true
84
- @options.default_config_file = true
85
- @options.config_file = File.expand_path(File.join(user_home_dir, CONFIG_FILE))
86
- @options.cache_file = File.expand_path(File.join(user_home_dir, CACHE_FILE))
87
- @options.command = nil
88
- end
89
-
90
- def validate_command_and_options
91
- if @command_name.nil? or argv.nil?
92
- log opt_parser.help
93
- exit
94
- end
95
-
96
- unless options.configured
97
- log "You must configure your swift credentials, take a look at:\n #{options.config_file}"
98
- exit
99
- end
100
- end
101
-
102
- def config_hash
103
- @config_hash ||= JSON.parse(File.read(options.config_file))
104
-
105
- rescue Errno::ENOENT => e
106
- log "No such file or directory - #{options.config_file}"
107
- exit 1
108
- end
109
-
110
- def execute_command
111
- NightcrawlerSwift.configure config_hash
112
- connect_and_execute do
113
- if command = COMMANDS[@command_name]
114
- command_module = command[:command]
115
- command_method = "command_#{@command_name}"
116
- self.send(command_method, command_module)
117
- end
118
- end
119
-
120
- rescue Exceptions::BaseError => e
121
- log e.message
122
- exit 1
123
- end
124
-
125
- def connect_and_execute &block
126
- path = options.cache_file
127
- if File.exist?(path)
128
- hash = JSON.parse File.read(path)
129
- NightcrawlerSwift.connection.auth_response = OpenStruct.new(hash)
130
- NightcrawlerSwift.connection.configure
131
- end
132
-
133
- begin
134
- block.call
135
- ensure
136
- File.open(path, "w") do |f|
137
- f.write(NightcrawlerSwift.connection.auth_response.to_h.to_json)
138
- end
139
- end
140
- end
141
-
142
- def parse_parameters
143
- configure_opt_parser
144
- opt_parser.parse!(argv)
145
- check_rcfile if options.default_config_file
146
-
147
- rescue OptionParser::InvalidOption => e
148
- log e.message
149
- exit 1
150
- end
151
-
152
- def configure_opt_parser
153
- @opt_parser = OptionParser.new do |opts|
154
- opts.banner = "nswift #{NightcrawlerSwift::VERSION}"
155
- opts.separator "Usage: nswift command [options]"
156
-
157
- opts.separator ""
158
- opts.separator "commands:"
159
- COMMANDS.keys.each do |key|
160
- opts.separator " #{key}\t\t\t #{COMMANDS[key][:description]}"
161
- end
162
-
163
- opts.separator ""
164
- opts.separator "options:"
165
-
166
- opts.on("-c", "--config=PATH", String, "Alternative '#{CONFIG_FILE}' file") do |path|
167
- path = File.expand_path(path.strip)
168
- log "Using custom config file at: #{path}"
169
- options.config_file = path
170
- options.default_config_file = false
171
- end
172
-
173
- opts.on_tail("-h", "--help", "Show this message") do
174
- log opts.help
175
- exit
176
- end
177
-
178
- opts.on_tail("-v", "--version", "Show version") do
179
- log NightcrawlerSwift::VERSION
180
- exit
181
- end
182
- end
183
- end
184
-
185
- def check_rcfile
186
- unless File.exist?(options.config_file)
187
- File.open(options.config_file, "w") { |f|
188
- f.write(sample_rcfile)
189
- }
190
- end
191
-
192
- if sample_rcfile == File.read(options.config_file)
193
- options.configured = false
194
- end
195
- end
196
-
197
- def sample_rcfile
41
+ def self.sample_rcfile
198
42
  JSON.pretty_generate({
199
43
  bucket: "<bucket/container name>",
200
44
  tenant_name: "<tenant name>",
@@ -203,6 +47,5 @@ module NightcrawlerSwift
203
47
  auth_url: "<auth url, ex: https://auth.url.com:123/v2.0/tokens>"
204
48
  })
205
49
  end
206
-
207
50
  end
208
51
  end
@@ -11,40 +11,31 @@ module NightcrawlerSwift
11
11
  NightcrawlerSwift.options
12
12
  end
13
13
 
14
+ # :nocov:
14
15
  def execute
15
16
  raise NotImplemented.new
16
17
  end
18
+ # :nocov:
17
19
 
18
20
  protected
19
21
 
20
22
  def get url, params = {}
21
23
  prepare_params params
22
- resource = resource_for url
23
- resource.get(params[:headers])
24
+ Gateway.new(url).request {|r| r.get params[:headers]}
24
25
  end
25
26
 
26
27
  def put url, params = {}
27
28
  prepare_params params
28
- resource = resource_for url
29
- resource.put(params[:body], params[:headers])
29
+ Gateway.new(url).request {|r| r.put params[:body], params[:headers]}
30
30
  end
31
31
 
32
32
  def delete url, params
33
33
  prepare_params params
34
- resource = resource_for url
35
- resource.delete(params[:headers])
34
+ Gateway.new(url).request {|r| r.delete params[:headers]}
36
35
  end
37
36
 
38
37
  private
39
38
 
40
- def resource_for url
41
- RestClient::Resource.new(
42
- url,
43
- verify_ssl: options.verify_ssl,
44
- timeout: options.timeout
45
- )
46
- end
47
-
48
39
  def prepare_params params
49
40
  params[:headers] ||= {}
50
41
  params[:headers]["X-Storage-Token"] = connection.token_id
@@ -2,14 +2,12 @@ module NightcrawlerSwift
2
2
  class Delete < Command
3
3
 
4
4
  def execute path
5
+ if path.nil? or path.empty?
6
+ raise Exceptions::ValidationError.new "Delete command requires a path parameter"
7
+ end
8
+
5
9
  response = delete "#{connection.upload_url}/#{path}", headers: {accept: :json }
6
10
  [200, 201].include?(response.code)
7
-
8
- rescue RestClient::ResourceNotFound => e
9
- raise Exceptions::NotFoundError.new(e)
10
-
11
- rescue StandardError => e
12
- raise Exceptions::ConnectionError.new(e)
13
11
  end
14
12
 
15
13
  end
@@ -2,14 +2,12 @@ module NightcrawlerSwift
2
2
  class Download < Command
3
3
 
4
4
  def execute path
5
+ if path.nil? or path.empty?
6
+ raise Exceptions::ValidationError.new "Download command requires a path parameter"
7
+ end
8
+
5
9
  response = get "#{connection.public_url}/#{options.bucket}/#{path}"
6
10
  response.body
7
-
8
- rescue RestClient::ResourceNotFound => e
9
- raise Exceptions::NotFoundError.new(e)
10
-
11
- rescue StandardError => e
12
- raise Exceptions::ConnectionError.new(e)
13
11
  end
14
12
 
15
13
  end
@@ -4,12 +4,6 @@ module NightcrawlerSwift
4
4
  def execute
5
5
  response = get connection.upload_url, headers: {accept: :json}
6
6
  JSON.parse(response.body)
7
-
8
- rescue RestClient::ResourceNotFound => e
9
- raise Exceptions::NotFoundError.new(e)
10
-
11
- rescue StandardError => e
12
- raise Exceptions::ConnectionError.new(e)
13
7
  end
14
8
 
15
9
  end
@@ -4,12 +4,9 @@ module NightcrawlerSwift
4
4
  def execute path, file
5
5
  content = file.read
6
6
  headers = {etag: etag(content), content_type: content_type(file)}
7
- headers.merge!(cache_control: "max-age=#{options.max_age}") if options.max_age
7
+ headers.merge!(cache_control: "public, max-age=#{options.max_age}") if options.max_age
8
8
  response = put "#{connection.upload_url}/#{path}", body: content, headers: headers
9
9
  [200, 201].include?(response.code)
10
-
11
- rescue RestClient::UnprocessableEntity => e
12
- raise Exceptions::ValidationError.new(e)
13
10
  end
14
11
 
15
12
  private
@@ -7,7 +7,7 @@ module NightcrawlerSwift
7
7
  authenticate!
8
8
  configure
9
9
 
10
- NightcrawlerSwift.logger.debug "[NightcrawlerSwift] Connected, token_id: #{token_id}"
10
+ NightcrawlerSwift.logger.debug "[NightcrawlerSwift] Connected, token_id: #{token_id}"
11
11
  self
12
12
  end
13
13
 
@@ -28,24 +28,24 @@ module NightcrawlerSwift
28
28
  end
29
29
 
30
30
  def authenticate!
31
- auth_options = {
32
- tenantName: opts.tenant_name,
33
- passwordCredentials: {username: opts.username, password: opts.password}
34
- }
35
-
36
- resource = RestClient::Resource.new(
37
- opts.auth_url,
38
- verify_ssl: NightcrawlerSwift.options.verify_ssl,
39
- timeout: NightcrawlerSwift.options.timeout
40
- )
41
-
42
- response = resource.post({ auth: auth_options }.to_json, content_type: :json, accept: :json)
31
+ url = opts.auth_url
32
+ headers = {content_type: :json, accept: :json}
33
+ response = Gateway.new(url).request {|r| r.post(auth_options.to_json, headers)}
43
34
 
44
35
  @auth_response = OpenStruct.new(JSON.parse(response.body))
45
36
  rescue StandardError => e
46
37
  raise Exceptions::ConnectionError.new(e)
47
38
  end
48
39
 
40
+ def auth_options
41
+ {
42
+ auth: {
43
+ tenantName: opts.tenant_name,
44
+ passwordCredentials: {username: opts.username, password: opts.password}
45
+ }
46
+ }
47
+ end
48
+
49
49
  def select_token
50
50
  @token_id = auth_response.access["token"]["id"]
51
51
  @expires_at = auth_response.access["token"]["expires"]
@@ -63,9 +63,9 @@ module NightcrawlerSwift
63
63
  end
64
64
 
65
65
  def configure_urls
66
- @admin_url = @endpoints["adminURL"]
66
+ @admin_url = opts.admin_url || @endpoints["adminURL"]
67
+ @public_url = opts.public_url || @endpoints["publicURL"]
67
68
  @upload_url = "#{@admin_url}/#{opts.bucket}"
68
- @public_url = @endpoints["publicURL"]
69
69
  end
70
70
  end
71
71
  end
@@ -5,12 +5,13 @@ module NightcrawlerSwift
5
5
  attr_accessor :original_exception
6
6
 
7
7
  def initialize exception
8
- super(exception.message)
8
+ super(exception.is_a?(String) ? exception : exception.message)
9
9
  @original_exception = exception
10
10
  end
11
11
  end
12
12
 
13
13
  class ConnectionError < BaseError; end
14
+ class UnauthorizedError < ConnectionError; end
14
15
  class ValidationError < ConnectionError; end
15
16
  class NotFoundError < BaseError; end
16
17
  class ConfigurationError < StandardError; end
@@ -0,0 +1,68 @@
1
+ module NightcrawlerSwift
2
+ class Gateway
3
+
4
+ attr_reader :resource, :attempts, :current_retry_time
5
+
6
+ RETRY_BLACKLIST = [
7
+ RestClient::Unauthorized,
8
+ RestClient::ResourceNotFound,
9
+ RestClient::UnprocessableEntity
10
+ ]
11
+
12
+ def initialize url
13
+ @url = url
14
+ @attempts = 0
15
+ @current_retry_time = 1
16
+ @retries = NightcrawlerSwift.options.retries
17
+ @max_retry_time = NightcrawlerSwift.options.max_retry_time
18
+
19
+ @resource = RestClient::Resource.new(
20
+ @url,
21
+ verify_ssl: NightcrawlerSwift.options.verify_ssl,
22
+ timeout: NightcrawlerSwift.options.timeout
23
+ )
24
+ end
25
+
26
+ def request &block
27
+ begin
28
+ @attempts += 1
29
+ block.call(resource)
30
+
31
+ rescue => e
32
+ raise e unless recoverable?(e)
33
+ wait(e) and retry
34
+ end
35
+
36
+ rescue RestClient::Unauthorized => e
37
+ raise Exceptions::UnauthorizedError.new(e)
38
+
39
+ rescue RestClient::ResourceNotFound => e
40
+ raise Exceptions::NotFoundError.new(e)
41
+
42
+ rescue RestClient::UnprocessableEntity => e
43
+ raise Exceptions::ValidationError.new(e)
44
+
45
+ rescue => e
46
+ raise Exceptions::ConnectionError.new(e)
47
+ end
48
+
49
+ private
50
+ def log message
51
+ NightcrawlerSwift.logger.debug message
52
+ end
53
+
54
+ def recoverable? e
55
+ @retries and
56
+ !RETRY_BLACKLIST.include?(e.class) and
57
+ @attempts <= @retries
58
+ end
59
+
60
+ def wait e
61
+ number = "#{@attempts}/#{@retries}"
62
+ log "Attempt #{number} to call '#{@url}', waiting #{@current_retry_time}s and retrying. Error: #{e.message}"
63
+ sleep @current_retry_time
64
+ @current_retry_time = [@current_retry_time * 2, @max_retry_time].min
65
+ end
66
+
67
+ end
68
+ end
@@ -1,3 +1,3 @@
1
1
  module NightcrawlerSwift
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -6,8 +6,9 @@ require "multi_mime"
6
6
  require "rest_client"
7
7
  require "nightcrawler_swift/version"
8
8
  require "nightcrawler_swift/exceptions"
9
- require "nightcrawler_swift/command"
9
+ require "nightcrawler_swift/gateway"
10
10
  require "nightcrawler_swift/connection"
11
+ require "nightcrawler_swift/command"
11
12
  require "nightcrawler_swift/commands/upload"
12
13
  require "nightcrawler_swift/commands/download"
13
14
  require "nightcrawler_swift/commands/list"
@@ -36,8 +37,12 @@ module NightcrawlerSwift
36
37
  # - verify_ssl (optional, default: false)
37
38
  # - timeout (in seconds. Optional, default: nil)
38
39
  #
40
+ # - retries (default: 3)
41
+ # - max_retry_time (in seconds, default: 30)
42
+ #
39
43
  def configure opts = {}
40
- @options = OpenStruct.new({verify_ssl: false}.merge(opts))
44
+ defaults = {verify_ssl: false, retries: 5, max_retry_time: 30}
45
+ @options = OpenStruct.new(defaults.merge(opts))
41
46
 
42
47
  if @options.max_age and not @options.max_age.is_a?(Numeric)
43
48
  raise Exceptions::ConfigurationError.new "max_age should be an Integer"
@@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.6"
25
25
  spec.add_development_dependency "rake"
26
26
  spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "codeclimate-test-reporter"
27
28
  spec.add_development_dependency "byebug"
28
29
  end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+ require "nightcrawler_swift/cli"
3
+
4
+ describe NightcrawlerSwift::CLI::UrlFor do
5
+
6
+ let(:connection) { NightcrawlerSwift::Connection.new }
7
+ let(:token) { "token" }
8
+ let(:expires_at) { (DateTime.now + 60).to_time }
9
+ let(:public_url) { "server-url" }
10
+ let(:bucket) { "rogue" }
11
+
12
+ subject do
13
+ NightcrawlerSwift::CLI::UrlFor.new
14
+ end
15
+
16
+ before do
17
+ allow(NightcrawlerSwift).to receive(:connection).and_return(connection)
18
+ allow(connection).to receive(:token_id).and_return(token)
19
+ allow(connection).to receive(:expires_at).and_return(expires_at)
20
+ allow(connection).to receive(:public_url).and_return(public_url)
21
+ NightcrawlerSwift.configure bucket: bucket
22
+ end
23
+
24
+ describe "#execute" do
25
+ let :execute do
26
+ subject.execute "file_path"
27
+ end
28
+
29
+ it "returns the public url of the given path" do
30
+ expect(execute).to eql("server-url/#{bucket}/file_path")
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,117 @@
1
+ require "spec_helper"
2
+ require "nightcrawler_swift/cli"
3
+
4
+ describe NightcrawlerSwift::CLI::Formatters::Basic do
5
+
6
+ let :config_dir do
7
+ File.expand_path(File.join(File.dirname(__FILE__), "../../../../fixtures"))
8
+ end
9
+
10
+ let :filepath do
11
+ "testfile.txt"
12
+ end
13
+
14
+ let :runner do
15
+ instance_double "Runner"
16
+ end
17
+
18
+ let :command do
19
+ command_class.new
20
+ end
21
+
22
+ subject do
23
+ NightcrawlerSwift::CLI::Formatters::Basic.new runner
24
+ end
25
+
26
+ describe "#command_list" do
27
+ let :result do
28
+ [{
29
+ "hash"=>"c9df50d4a29542f8b6d426a50c72b3de",
30
+ "last_modified"=>"2014-08-27T19:35:46.053560",
31
+ "bytes"=>4994,
32
+ "name"=>"assets/file.png",
33
+ "content_type"=>"image/png"
34
+ }]
35
+ end
36
+
37
+ let :command_class do
38
+ NightcrawlerSwift::List
39
+ end
40
+
41
+ it "lists all files in the bucket/container configured" do
42
+ expect(command_class).to receive(:new).and_return(command)
43
+ expect(command).to receive(:execute).and_return(result)
44
+ expect(runner).to receive(:log).with(result.first["name"])
45
+ subject.command_list command_class
46
+ end
47
+ end
48
+
49
+ describe "#command_download" do
50
+ let :command_class do
51
+ NightcrawlerSwift::Download
52
+ end
53
+
54
+ it "downloads the file" do
55
+ expect(command_class).to receive(:new).and_return(command)
56
+ expect(command).to receive(:execute).with(filepath).and_return("test-content")
57
+ expect(runner).to receive(:log).with("test-content")
58
+ expect(runner).to receive(:argv).and_return([filepath])
59
+ subject.command_download command_class
60
+ end
61
+ end
62
+
63
+ describe "#command_upload" do
64
+ let :realpath do
65
+ File.join config_dir, filepath
66
+ end
67
+
68
+ let :command_class do
69
+ NightcrawlerSwift::Upload
70
+ end
71
+
72
+ before do
73
+ File.open(realpath, "w") {|f| f.write("test") }
74
+ end
75
+
76
+ after do
77
+ File.delete(realpath) if File.exist?(realpath)
78
+ end
79
+
80
+ it "uploads the file" do
81
+ expect(command_class).to receive(:new).and_return(command)
82
+ expect(command).to receive(:execute).with(filepath, instance_of(File)).and_return(true)
83
+ expect(runner).to receive(:log).with("success")
84
+ expect(runner).to receive(:argv).twice.and_return([realpath, filepath])
85
+ subject.command_upload command_class
86
+ end
87
+ end
88
+
89
+ describe "#command_delete" do
90
+ let :command_class do
91
+ NightcrawlerSwift::Delete
92
+ end
93
+
94
+ it "deletes the file" do
95
+ expect(command_class).to receive(:new).and_return(command)
96
+ expect(command).to receive(:execute).with(filepath).and_return(true)
97
+ expect(runner).to receive(:log).with("success")
98
+ expect(runner).to receive(:argv).and_return([filepath])
99
+ subject.command_delete command_class
100
+ end
101
+ end
102
+
103
+ describe "#command_url_for" do
104
+ let :command_class do
105
+ NightcrawlerSwift::CLI::UrlFor
106
+ end
107
+
108
+ it "returns the public url of the given path" do
109
+ expect(command_class).to receive(:new).and_return(command)
110
+ expect(command).to receive(:execute).with(filepath).and_return("public-url")
111
+ expect(runner).to receive(:log).with("public-url")
112
+ expect(runner).to receive(:argv).and_return([filepath])
113
+ subject.command_url_for command_class
114
+ end
115
+ end
116
+
117
+ end