turbot 0.0.2

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 (71) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +36 -0
  3. data/bin/turbot +17 -0
  4. data/data/cacert.pem +3988 -0
  5. data/lib/turbot/auth.rb +315 -0
  6. data/lib/turbot/cli.rb +38 -0
  7. data/lib/turbot/client/cisaurus.rb +25 -0
  8. data/lib/turbot/client/pgbackups.rb +113 -0
  9. data/lib/turbot/client/rendezvous.rb +111 -0
  10. data/lib/turbot/client/ssl_endpoint.rb +25 -0
  11. data/lib/turbot/client/turbot_postgresql.rb +148 -0
  12. data/lib/turbot/client.rb +757 -0
  13. data/lib/turbot/command/auth.rb +85 -0
  14. data/lib/turbot/command/base.rb +192 -0
  15. data/lib/turbot/command/bots.rb +326 -0
  16. data/lib/turbot/command/config.rb +123 -0
  17. data/lib/turbot/command/help.rb +179 -0
  18. data/lib/turbot/command/keys.rb +115 -0
  19. data/lib/turbot/command/logs.rb +34 -0
  20. data/lib/turbot/command/ssl.rb +43 -0
  21. data/lib/turbot/command/status.rb +51 -0
  22. data/lib/turbot/command/update.rb +47 -0
  23. data/lib/turbot/command/version.rb +23 -0
  24. data/lib/turbot/command.rb +304 -0
  25. data/lib/turbot/deprecated/help.rb +38 -0
  26. data/lib/turbot/deprecated.rb +5 -0
  27. data/lib/turbot/distribution.rb +9 -0
  28. data/lib/turbot/errors.rb +28 -0
  29. data/lib/turbot/excon.rb +11 -0
  30. data/lib/turbot/helpers/log_displayer.rb +70 -0
  31. data/lib/turbot/helpers/pg_dump_restore.rb +115 -0
  32. data/lib/turbot/helpers/turbot_postgresql.rb +213 -0
  33. data/lib/turbot/helpers.rb +521 -0
  34. data/lib/turbot/plugin.rb +165 -0
  35. data/lib/turbot/updater.rb +171 -0
  36. data/lib/turbot/version.rb +3 -0
  37. data/lib/turbot.rb +19 -0
  38. data/lib/vendor/turbot/okjson.rb +598 -0
  39. data/spec/helper/legacy_help.rb +16 -0
  40. data/spec/helper/pg_dump_restore_spec.rb +67 -0
  41. data/spec/schemas/dummy_schema.json +12 -0
  42. data/spec/spec.opts +1 -0
  43. data/spec/spec_helper.rb +220 -0
  44. data/spec/support/display_message_matcher.rb +49 -0
  45. data/spec/support/dummy_api.rb +120 -0
  46. data/spec/support/openssl_mock_helper.rb +8 -0
  47. data/spec/support/organizations_mock_helper.rb +11 -0
  48. data/spec/turbot/auth_spec.rb +214 -0
  49. data/spec/turbot/client/pgbackups_spec.rb +43 -0
  50. data/spec/turbot/client/rendezvous_spec.rb +62 -0
  51. data/spec/turbot/client/ssl_endpoint_spec.rb +48 -0
  52. data/spec/turbot/client/turbot_postgresql_spec.rb +71 -0
  53. data/spec/turbot/client_spec.rb +548 -0
  54. data/spec/turbot/command/auth_spec.rb +38 -0
  55. data/spec/turbot/command/base_spec.rb +66 -0
  56. data/spec/turbot/command/bots_spec.rb +54 -0
  57. data/spec/turbot/command/config_spec.rb +143 -0
  58. data/spec/turbot/command/help_spec.rb +90 -0
  59. data/spec/turbot/command/keys_spec.rb +117 -0
  60. data/spec/turbot/command/logs_spec.rb +60 -0
  61. data/spec/turbot/command/status_spec.rb +48 -0
  62. data/spec/turbot/command/version_spec.rb +16 -0
  63. data/spec/turbot/command_spec.rb +131 -0
  64. data/spec/turbot/helpers/turbot_postgresql_spec.rb +181 -0
  65. data/spec/turbot/helpers_spec.rb +48 -0
  66. data/spec/turbot/plugin_spec.rb +172 -0
  67. data/spec/turbot/updater_spec.rb +44 -0
  68. data/templates/manifest.json +7 -0
  69. data/templates/scraper.py +5 -0
  70. data/templates/scraper.rb +6 -0
  71. metadata +199 -0
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+ require "turbot/command/bots"
3
+
4
+ describe Turbot::Command::Bots do
5
+ describe "validate" do
6
+ before do
7
+ Turbot::Command::Bots.any_instance.stub(:parsed_manifest).and_return('data_type' => 'dummy')
8
+ end
9
+
10
+ context "for data_type with schema" do
11
+ before do
12
+ Turbot::Command::Bots.any_instance.
13
+ stub(:get_schema).
14
+ and_return(File.expand_path('../../../schemas/dummy_schema.json', __FILE__))
15
+
16
+ end
17
+
18
+ it "says bot is valid if its output matches the schema" do
19
+ Turbot::Command::Bots.any_instance.
20
+ stub(:run_scraper_each_line).
21
+ and_yield({name: 'One'}.to_json)
22
+
23
+ stderr, stdout = execute("bots:validate")
24
+
25
+ stderr.should == ""
26
+ stdout.should include 'Validated 1 records successfully'
27
+ end
28
+
29
+ it "says bot is invalid if its output doesn't match the schema" do
30
+ Turbot::Command::Bots.any_instance.
31
+ stub(:run_scraper_each_line).
32
+ and_yield({name: 123}.to_json)
33
+
34
+ stderr, stdout = execute("bots:validate")
35
+
36
+ stdout.should == ""
37
+ stderr.should include 'ERRORS'
38
+ end
39
+ end
40
+
41
+ context "for data_type without schema" do
42
+ it "says bot is invalid" do
43
+ Turbot::Command::Bots.any_instance.
44
+ stub(:get_schema).
45
+ and_return(nil)
46
+
47
+ stderr, stdout = execute("bots:validate")
48
+
49
+ stdout.should == ""
50
+ stderr.should include 'No schema found'
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,143 @@
1
+ require "spec_helper"
2
+ require "turbot/command/config"
3
+
4
+ module Turbot::Command
5
+ describe Config do
6
+ before(:each) do
7
+ stub_core
8
+ api.post_bot("name" => "example", "stack" => "cedar")
9
+ end
10
+
11
+ after(:each) do
12
+ api.delete_bot("example")
13
+ end
14
+
15
+ it "shows all configs" do
16
+ api.put_config_vars("example", { 'FOO_BAR' => 'one', 'BAZ_QUX' => 'two' })
17
+ stderr, stdout = execute("config")
18
+ stderr.should == ""
19
+ stdout.should == <<-STDOUT
20
+ === example Config Vars
21
+ BAZ_QUX: two
22
+ FOO_BAR: one
23
+ STDOUT
24
+ end
25
+
26
+ it "does not trim long values" do
27
+ api.put_config_vars("example", { 'LONG' => 'A' * 60 })
28
+ stderr, stdout = execute("config")
29
+ stderr.should == ""
30
+ stdout.should == <<-STDOUT
31
+ === example Config Vars
32
+ LONG: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
33
+ STDOUT
34
+ end
35
+
36
+ it "handles when value is nil" do
37
+ api.put_config_vars("example", { 'FOO_BAR' => 'one', 'BAZ_QUX' => nil })
38
+ stderr, stdout = execute("config")
39
+ stderr.should == ""
40
+ stdout.should == <<-STDOUT
41
+ === example Config Vars
42
+ BAZ_QUX: \nFOO_BAR: one
43
+ STDOUT
44
+ end
45
+
46
+ it "handles when value is a boolean" do
47
+ api.put_config_vars("example", { 'FOO_BAR' => 'one', 'BAZ_QUX' => true })
48
+ stderr, stdout = execute("config")
49
+ stderr.should == ""
50
+ stdout.should == <<-STDOUT
51
+ === example Config Vars
52
+ BAZ_QUX: true
53
+ FOO_BAR: one
54
+ STDOUT
55
+ end
56
+
57
+ it "shows configs in a shell compatible format" do
58
+ api.put_config_vars("example", { 'A' => 'one', 'B' => 'two three' })
59
+ stderr, stdout = execute("config --shell")
60
+ stderr.should == ""
61
+ stdout.should == <<-STDOUT
62
+ A=one
63
+ B=two three
64
+ STDOUT
65
+ end
66
+
67
+ it "shows a single config for get" do
68
+ api.put_config_vars("example", { 'LONG' => 'A' * 60 })
69
+ stderr, stdout = execute("config:get LONG")
70
+ stderr.should == ""
71
+ stdout.should == <<-STDOUT
72
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
73
+ STDOUT
74
+ end
75
+
76
+ context("set") do
77
+
78
+ it "sets config vars" do
79
+ stderr, stdout = execute("config:set A=1 B=2")
80
+ stderr.should == ""
81
+ stdout.should == <<-STDOUT
82
+ Setting config vars and restarting example... done
83
+ A: 1
84
+ B: 2
85
+ STDOUT
86
+ end
87
+
88
+ it "allows config vars with = in the value" do
89
+ stderr, stdout = execute("config:set A=b=c")
90
+ stderr.should == ""
91
+ stdout.should == <<-STDOUT
92
+ Setting config vars and restarting example... done
93
+ A: b=c
94
+ STDOUT
95
+ end
96
+
97
+ it "sets config vars without changing case" do
98
+ stderr, stdout = execute("config:set a=b")
99
+ stderr.should == ""
100
+ stdout.should == <<-STDOUT
101
+ Setting config vars and restarting example... done
102
+ a: b
103
+ STDOUT
104
+ end
105
+
106
+ end
107
+
108
+ describe "config:unset" do
109
+
110
+ it "exits with a help notice when no keys are provides" do
111
+ stderr, stdout = execute("config:unset")
112
+ stderr.should == <<-STDERR
113
+ ! Usage: turbot config:unset KEY1 [KEY2 ...]
114
+ ! Must specify KEY to unset.
115
+ STDERR
116
+ stdout.should == ""
117
+ end
118
+
119
+ context "when one key is provided" do
120
+
121
+ it "unsets a single key" do
122
+ stderr, stdout = execute("config:unset A")
123
+ stderr.should == ""
124
+ stdout.should == <<-STDOUT
125
+ Unsetting A and restarting example... done
126
+ STDOUT
127
+ end
128
+ end
129
+
130
+ context "when more than one key is provided" do
131
+
132
+ it "unsets all given keys" do
133
+ stderr, stdout = execute("config:unset A B")
134
+ stderr.should == ""
135
+ stdout.should == <<-STDOUT
136
+ Unsetting A and restarting example... done
137
+ Unsetting B and restarting example... done
138
+ STDOUT
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,90 @@
1
+ require "spec_helper"
2
+ require "turbot/command/bots"
3
+ require "turbot/command/help"
4
+
5
+ describe Turbot::Command::Help do
6
+
7
+ describe "help" do
8
+ it "should show root help with no args" do
9
+ stderr, stdout = execute("help")
10
+ stderr.should == ""
11
+ stdout.should include "Usage: turbot COMMAND [--bot APP] [command-specific-options]"
12
+ stdout.should include "bots"
13
+ stdout.should include "help"
14
+ end
15
+
16
+ it "should show command help and namespace help when ambigious" do
17
+ stderr, stdout = execute("help bots")
18
+ stderr.should == ""
19
+ stdout.should include "turbot bots"
20
+ stdout.should include "list your bots"
21
+ stdout.should include "Additional commands"
22
+ stdout.should include "bots:info"
23
+ end
24
+
25
+ it "should show only command help when not ambiguous" do
26
+ stderr, stdout = execute("help bots:info")
27
+ stderr.should == ""
28
+ stdout.should include "turbot bots:info"
29
+ stdout.should_not include "Additional commands"
30
+ end
31
+
32
+ it "should show command help with --help" do
33
+ stderr, stdout = execute("bots:info --help")
34
+ stderr.should == ""
35
+ stdout.should include "Usage: turbot bots:info"
36
+ stdout.should_not include "Additional commands"
37
+ end
38
+
39
+ it "should redirect if the command is an alias" do
40
+ stderr, stdout = execute("help list")
41
+ stderr.should == ""
42
+ stdout.should include "Alias: list redirects to bots"
43
+ stdout.should include "Usage: turbot bots"
44
+ stdout.should include "list your bots"
45
+ end
46
+
47
+ it "should show if the command does not exist" do
48
+ stderr, stdout = execute("help sudo:sandwich")
49
+ stderr.should == <<-STDERR
50
+ ! sudo:sandwich is not a turbot command. See `turbot help`.
51
+ STDERR
52
+ stdout.should == ""
53
+ end
54
+
55
+ it "should show help with naked -h" do
56
+ stderr, stdout = execute("-h")
57
+ stderr.should == ""
58
+ stdout.should include "Usage: turbot COMMAND"
59
+ end
60
+
61
+ it "should show help with naked --help" do
62
+ stderr, stdout = execute("--help")
63
+ stderr.should == ""
64
+ stdout.should include "Usage: turbot COMMAND"
65
+ end
66
+
67
+ describe "with legacy help" do
68
+ require "helper/legacy_help"
69
+
70
+ it "displays the legacy group in the namespace list" do
71
+ stderr, stdout = execute("help")
72
+ stderr.should == ""
73
+ stdout.should include "Foo Group"
74
+ end
75
+
76
+ it "displays group help" do
77
+ stderr, stdout = execute("help foo")
78
+ stderr.should == ""
79
+ stdout.should include "do a bar to foo"
80
+ stdout.should include "do a baz to foo"
81
+ end
82
+
83
+ it "displays legacy command-specific help" do
84
+ stderr, stdout = execute("help foo:bar")
85
+ stderr.should == ""
86
+ stdout.should include "do a bar to foo"
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,117 @@
1
+ require "spec_helper"
2
+ require "turbot/command/keys"
3
+
4
+ module Turbot::Command
5
+ describe Keys do
6
+ KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAp9AJD5QABmOcrkHm6SINuQkDefaR0MUrfgZ1Pxir3a4fM1fwa00dsUwbUaRuR7FEFD8n1E9WwDf8SwQTHtyZsJg09G9myNqUzkYXCmydN7oGr5IdVhRyv5ixcdiE0hj7dRnOJg2poSQ3Qi+Ka8SVJzF7nIw1YhuicHPSbNIFKi5s0D5a+nZb/E6MNGvhxoFCQX2IcNxaJMqhzy1ESwlixz45aT72mXYq0LIxTTpoTqma1HuKdRY8HxoREiivjmMQulYP+CxXFcMyV9kxTKIUZ/FXqlC6G5vSm3J4YScSatPOj9ID5HowpdlIx8F6y4p1/28r2tTl4CY40FFyoke4MQ== pedro@turbot"
7
+
8
+ before(:each) do
9
+ stub_core
10
+ end
11
+
12
+ after(:each) do
13
+ api.delete_keys
14
+ end
15
+
16
+ context("add") do
17
+ it "tries to find a key if no key filename is supplied" do
18
+ Turbot::Auth.should_receive(:ask).and_return("y")
19
+ Turbot::Auth.should_receive(:generate_ssh_key)
20
+ File.should_receive(:exists?).with('/.ssh/id_rsa.pub').and_return(true)
21
+ File.should_receive(:read).with('/.ssh/id_rsa.pub').and_return(KEY)
22
+ stderr, stdout = execute("keys:add")
23
+ stderr.should == ""
24
+ stdout.should == <<-STDOUT
25
+ Could not find an existing public key.
26
+ Would you like to generate one? [Yn] Generating new SSH public key.
27
+ Uploading SSH public key /.ssh/id_rsa.pub... done
28
+ STDOUT
29
+ end
30
+
31
+ it "adds a key from a specified keyfile path" do
32
+ File.should_receive(:exists?).with('/my/key.pub').and_return(true)
33
+ File.should_receive(:read).with('/my/key.pub').and_return(KEY)
34
+ stderr, stdout = execute("keys:add /my/key.pub")
35
+ stderr.should == ""
36
+ stdout.should == <<-STDOUT
37
+ Uploading SSH public key /my/key.pub... done
38
+ STDOUT
39
+ end
40
+
41
+ end
42
+
43
+ context("index") do
44
+
45
+ before do
46
+ api.post_key(KEY)
47
+ end
48
+
49
+ after do
50
+ api.delete_keys
51
+ end
52
+
53
+ it "list keys, trimming the hex code for better display" do
54
+ stderr, stdout = execute("keys")
55
+ stderr.should == ""
56
+ stdout.should == <<-STDOUT
57
+ === email@example.com Keys
58
+ ssh-rsa AAAAB3NzaC...Fyoke4MQ== pedro@turbot
59
+
60
+ STDOUT
61
+ end
62
+
63
+ it "list keys showing the whole key hex with --long" do
64
+ stderr, stdout = execute("keys --long")
65
+ stderr.should == ""
66
+ stdout.should == <<-STDOUT
67
+ === email@example.com Keys
68
+ ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAp9AJD5QABmOcrkHm6SINuQkDefaR0MUrfgZ1Pxir3a4fM1fwa00dsUwbUaRuR7FEFD8n1E9WwDf8SwQTHtyZsJg09G9myNqUzkYXCmydN7oGr5IdVhRyv5ixcdiE0hj7dRnOJg2poSQ3Qi+Ka8SVJzF7nIw1YhuicHPSbNIFKi5s0D5a+nZb/E6MNGvhxoFCQX2IcNxaJMqhzy1ESwlixz45aT72mXYq0LIxTTpoTqma1HuKdRY8HxoREiivjmMQulYP+CxXFcMyV9kxTKIUZ/FXqlC6G5vSm3J4YScSatPOj9ID5HowpdlIx8F6y4p1/28r2tTl4CY40FFyoke4MQ== pedro@turbot
69
+
70
+ STDOUT
71
+ end
72
+
73
+ end
74
+
75
+ context("remove") do
76
+
77
+ context("success") do
78
+
79
+ before(:each) do
80
+ api.post_key(KEY)
81
+ end
82
+
83
+ it "succeeds" do
84
+ stderr, stdout = execute("keys:remove pedro@turbot")
85
+ stderr.should == ""
86
+ stdout.should == <<-STDOUT
87
+ Removing pedro@turbot SSH key... done
88
+ STDOUT
89
+ end
90
+
91
+ end
92
+
93
+ it "displays an error if no key is specified" do
94
+ stderr, stdout = execute("keys:remove")
95
+ stderr.should == <<-STDERR
96
+ ! Usage: turbot keys:remove KEY
97
+ ! Must specify KEY to remove.
98
+ STDERR
99
+ stdout.should == ""
100
+ end
101
+
102
+ end
103
+
104
+ context("clear") do
105
+
106
+ it "succeeds" do
107
+ stderr, stdout = execute("keys:clear")
108
+ stderr.should == ""
109
+ stdout.should == <<-STDOUT
110
+ Removing all SSH keys... done
111
+ STDOUT
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+ require "turbot/command/logs"
3
+
4
+ describe Turbot::Command::Logs do
5
+ describe "logs" do
6
+ it "runs with no options" do
7
+ stub_core.read_logs("example", [])
8
+ execute "logs"
9
+ end
10
+
11
+ it "runs with options" do
12
+ stub_core.read_logs("example", [
13
+ "tail=1",
14
+ "num=2",
15
+ "ps=ps.3",
16
+ "source=source.4"
17
+ ])
18
+ execute "logs --tail --num 2 --ps ps.3 --source source.4"
19
+ end
20
+
21
+ describe "with log output" do
22
+ before(:each) do
23
+ stub_core.read_logs("example", []).yields("2011-01-01T00:00:00+00:00 bot[web.1]: test")
24
+ end
25
+
26
+ it "prettifies tty output" do
27
+ old_stdout_isatty = $stdout.isatty
28
+ stub($stdout).isatty.returns(true)
29
+ stderr, stdout = execute("logs")
30
+ stderr.should == ""
31
+ stdout.should == <<-STDOUT
32
+ \e[36m2011-01-01T00:00:00+00:00 bot[web.1]:\e[0m test
33
+ STDOUT
34
+ stub($stdout).isatty.returns(old_stdout_isatty)
35
+ end
36
+
37
+ it "does not use ansi if stdout is not a tty" do
38
+ old_stdout_isatty = $stdout.isatty
39
+ stub($stdout).isatty.returns(false)
40
+ stderr, stdout = execute("logs")
41
+ stderr.should == ""
42
+ stdout.should == <<-STDOUT
43
+ 2011-01-01T00:00:00+00:00 bot[web.1]: test
44
+ STDOUT
45
+ stub($stdout).isatty.returns(old_stdout_isatty)
46
+ end
47
+
48
+ it "does not use ansi if TERM is not set" do
49
+ term = ENV.delete("TERM")
50
+ stderr, stdout = execute("logs")
51
+ stderr.should == ""
52
+ stdout.should == <<-STDOUT
53
+ 2011-01-01T00:00:00+00:00 bot[web.1]: test
54
+ STDOUT
55
+ ENV["TERM"] = term
56
+ end
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,48 @@
1
+ require "spec_helper"
2
+ require "turbot/command/status"
3
+
4
+ module Turbot::Command
5
+ describe Status do
6
+
7
+ before(:each) do
8
+ stub_core
9
+ end
10
+
11
+ it "displays status information" do
12
+ Excon.stub(
13
+ {
14
+ :host => 'status.turbot.com',
15
+ :method => :get,
16
+ :path => '/api/v3/current-status.json'
17
+ },
18
+ {
19
+ :body => Turbot::OkJson.encode({"status"=>{"Production"=>"red", "Development"=>"red"}, "issues"=>[{"created_at"=>"2012-06-07T15:55:51Z", "id"=>372, "resolved"=>false, "title"=>"HTTP Routing Errors", "updated_at"=>"2012-06-07T16:14:37Z", "href"=>"https://status.turbot.com/api/v3/issues/372", "updates"=>[{"contents"=>"The number of applications seeing H99 errors is continuing to decrease as we continue to work toward a full resolution of the HTTP routing issues. The API is back online now as well. ", "created_at"=>"2012-06-07T17:47:26Z", "id"=>1088, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T17:47:26Z"}, {"contents"=>"Our engineers are continuing to work toward a full resolution of the HTTP routing issues. The API is currently in maintenance mode intentionally as we restore application operations. ", "created_at"=>"2012-06-07T17:16:40Z", "id"=>1086, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T17:26:55Z"}, {"contents"=>"Most applications are back online at this time. Our engineers are working on getting the remaining bots back online. ", "created_at"=>"2012-06-07T16:50:21Z", "id"=>1085, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:50:21Z"}, {"contents"=>"Our routing engineers have pushed out a patch to our routing tier. The platform is recovering and applications are coming back online. Our engineers are continuing to fully restore service.", "created_at"=>"2012-06-07T16:36:37Z", "id"=>1084, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:36:37Z"}, {"contents"=>"We have identified an issue with our routers that is causing errors on HTTP requests to applications. Engineers are working to resolve the issue.\r\n", "created_at"=>"2012-06-07T16:15:25Z", "id"=>1083, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:15:28Z"}, {"contents"=>"We have confirmed widespread errors on the platform. Our engineers are continuing to investigate.\r\n", "created_at"=>"2012-06-07T15:58:56Z", "id"=>1082, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T15:58:58Z"}, {"contents"=>"Our automated systems have detected potential platform errors. We are investigating.\r\n", "created_at"=>"2012-06-07T15:55:51Z", "id"=>1081, "incident_id"=>372, "status_dev"=>"yellow", "status_prod"=>"yellow", "update_type"=>"issue", "updated_at"=>"2012-06-07T15:55:55Z"}]}]}),
20
+ :status => 200
21
+ }
22
+ )
23
+
24
+ Turbot::Command::Status.any_instance.should_receive(:time_ago).and_return('2012/09/11 09:34:56 (~ 3h ago)', '2012/09/11 12:33:56 (~ 1m ago)', '2012/09/11 12:29:56 (~ 5m ago)', '2012/09/11 12:24:56 (~ 10m ago)', '2012/09/11 12:04:56 (~ 30m ago)', '2012/09/11 11:34:56 (~ 1h ago)', '2012/09/11 10:34:56 (~ 2h ago)', '2012/09/11 09:34:56 (~ 3h ago)')
25
+
26
+ stderr, stdout = execute("status")
27
+ stderr.should == ''
28
+ stdout.should == <<-STDOUT
29
+ === Turbot Status
30
+ Development: red
31
+ Production: red
32
+
33
+ === HTTP Routing Errors 2012/09/11 09:34:56 (~ 3h+)
34
+ 2012/09/11 12:33:56 (~ 1m ago) update The number of applications seeing H99 errors is continuing to decrease as we continue to work toward a full resolution of the HTTP routing issues. The API is back online now as well.
35
+ 2012/09/11 12:29:56 (~ 5m ago) update Our engineers are continuing to work toward a full resolution of the HTTP routing issues. The API is currently in maintenance mode intentionally as we restore application operations.
36
+ 2012/09/11 12:24:56 (~ 10m ago) update Most applications are back online at this time. Our engineers are working on getting the remaining bots back online.
37
+ 2012/09/11 12:04:56 (~ 30m ago) update Our routing engineers have pushed out a patch to our routing tier. The platform is recovering and applications are coming back online. Our engineers are continuing to fully restore service.
38
+ 2012/09/11 11:34:56 (~ 1h ago) update We have identified an issue with our routers that is causing errors on HTTP requests to applications. Engineers are working to resolve the issue.
39
+ 2012/09/11 10:34:56 (~ 2h ago) update We have confirmed widespread errors on the platform. Our engineers are continuing to investigate.
40
+ 2012/09/11 09:34:56 (~ 3h ago) issue Our automated systems have detected potential platform errors. We are investigating.
41
+
42
+ STDOUT
43
+
44
+ Excon.stubs.shift
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+ require "turbot/command/version"
3
+
4
+ module Turbot::Command
5
+ describe Version do
6
+
7
+ it "shows version info" do
8
+ stderr, stdout = execute("version")
9
+ stderr.should == ""
10
+ stdout.should == <<-STDOUT
11
+ #{Turbot.user_agent}
12
+ STDOUT
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,131 @@
1
+ require "spec_helper"
2
+ require "turbot/command"
3
+ require 'json' #FOR WEBMOCK
4
+
5
+ class FakeResponse
6
+
7
+ attr_accessor :body, :headers
8
+
9
+ def initialize(attributes)
10
+ self.body, self.headers = attributes[:body], attributes[:headers]
11
+ end
12
+
13
+ def to_s
14
+ body
15
+ end
16
+
17
+ end
18
+
19
+ describe Turbot::Command do
20
+ before {
21
+ Turbot::Command.load
22
+ stub_core # setup fake auth
23
+ }
24
+
25
+ describe "parsing errors" do
26
+ it "extracts error messages from response when available in XML" do
27
+ Turbot::Command.extract_error('<errors><error>Invalid bot name</error></errors>').should == 'Invalid bot name'
28
+ end
29
+
30
+ it "extracts error messages from response when available in JSON" do
31
+ Turbot::Command.extract_error("{\"error\":\"Invalid bot name\"}").should == 'Invalid bot name'
32
+ end
33
+
34
+ it "extracts error messages from response when available in plain text" do
35
+ response = FakeResponse.new(:body => "Invalid bot name", :headers => { :content_type => "text/plain; charset=UTF8" })
36
+ Turbot::Command.extract_error(response).should == 'Invalid bot name'
37
+ end
38
+
39
+ it "shows Internal Server Error when the response doesn't contain a XML or JSON" do
40
+ Turbot::Command.extract_error('<h1>HTTP 500</h1>').should == "Internal server error.\nRun `turbot status` to check for known platform issues."
41
+ end
42
+
43
+ it "shows Internal Server Error when the response is not plain text" do
44
+ response = FakeResponse.new(:body => "Foobar", :headers => { :content_type => "application/xml" })
45
+ Turbot::Command.extract_error(response).should == "Internal server error.\nRun `turbot status` to check for known platform issues."
46
+ end
47
+
48
+ it "allows a block to redefine the default error" do
49
+ Turbot::Command.extract_error("Foobar") { "Ok!" }.should == 'Ok!'
50
+ end
51
+
52
+ it "doesn't format the response if set to raw" do
53
+ Turbot::Command.extract_error("Foobar", :raw => true) { "Ok!" }.should == 'Ok!'
54
+ end
55
+
56
+ it "handles a nil body in parse_error_xml" do
57
+ lambda { Turbot::Command.parse_error_xml(nil) }.should_not raise_error
58
+ end
59
+
60
+ it "handles a nil body in parse_error_json" do
61
+ lambda { Turbot::Command.parse_error_json(nil) }.should_not raise_error
62
+ end
63
+ end
64
+
65
+ it "correctly resolves commands" do
66
+ class Turbot::Command::Test; end
67
+ class Turbot::Command::Test::Multiple; end
68
+
69
+ require "turbot/command/help"
70
+ require "turbot/command/bots"
71
+
72
+ Turbot::Command.parse("unknown").should be_nil
73
+ Turbot::Command.parse("list").should include(:klass => Turbot::Command::Bots, :method => :index)
74
+ Turbot::Command.parse("bots").should include(:klass => Turbot::Command::Bots, :method => :index)
75
+ Turbot::Command.parse("bots:info").should include(:klass => Turbot::Command::Bots, :method => :info)
76
+ end
77
+
78
+ context "help" do
79
+ it "works as a prefix" do
80
+ turbot("help bots:info").should =~ /show detailed bot information/
81
+ end
82
+
83
+ it "works as an option" do
84
+ turbot("bots:info -h").should =~ /show detailed bot information/
85
+ turbot("bots:info --help").should =~ /show detailed bot information/
86
+ end
87
+ end
88
+
89
+ context "when no commands match" do
90
+
91
+ it "displays the version if --version is used" do
92
+ turbot("--version").should == <<-STDOUT
93
+ #{Turbot.user_agent}
94
+ STDOUT
95
+ end
96
+
97
+ it "suggests similar commands if there are any" do
98
+ original_stderr, original_stdout = $stderr, $stdout
99
+ $stderr = captured_stderr = StringIO.new
100
+ $stdout = captured_stdout = StringIO.new
101
+ begin
102
+ execute("bot")
103
+ rescue SystemExit
104
+ end
105
+ captured_stderr.string.should == <<-STDERR
106
+ ! `bot` is not a turbot command.
107
+ ! Perhaps you meant `bots`.
108
+ ! See `turbot help` for a list of available commands.
109
+ STDERR
110
+ captured_stdout.string.should == ""
111
+ $stderr, $stdout = original_stderr, original_stdout
112
+ end
113
+
114
+ it "does not suggest similar commands if there are none" do
115
+ original_stderr, original_stdout = $stderr, $stdout
116
+ $stderr = captured_stderr = StringIO.new
117
+ $stdout = captured_stdout = StringIO.new
118
+ begin
119
+ execute("sandwich")
120
+ rescue SystemExit
121
+ end
122
+ captured_stderr.string.should == <<-STDERR
123
+ ! `sandwich` is not a turbot command.
124
+ ! See `turbot help` for a list of available commands.
125
+ STDERR
126
+ captured_stdout.string.should == ""
127
+ $stderr, $stdout = original_stderr, original_stdout
128
+ end
129
+
130
+ end
131
+ end