knife-google 1.3.1 → 2.0.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 (111) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +1 -0
  3. data/.travis.yml +15 -3
  4. data/CHANGELOG.md +4 -6
  5. data/Gemfile +3 -9
  6. data/README.md +208 -355
  7. data/RELEASE_NOTES.md +8 -17
  8. data/Rakefile +8 -49
  9. data/knife-google.gemspec +20 -17
  10. data/lib/chef/knife/cloud/google_service.rb +491 -0
  11. data/lib/chef/knife/cloud/google_service_helpers.rb +62 -0
  12. data/lib/chef/knife/cloud/google_service_options.rb +58 -0
  13. data/lib/chef/knife/google_disk_create.rb +40 -44
  14. data/lib/chef/knife/google_disk_delete.rb +22 -40
  15. data/lib/chef/knife/google_disk_list.rb +57 -51
  16. data/lib/chef/knife/google_project_quotas.rb +59 -0
  17. data/lib/chef/knife/google_region_list.rb +43 -102
  18. data/lib/chef/knife/google_region_quotas.rb +77 -0
  19. data/lib/chef/knife/google_server_create.rb +224 -505
  20. data/lib/chef/knife/google_server_delete.rb +20 -78
  21. data/lib/chef/knife/google_server_list.rb +42 -53
  22. data/lib/chef/knife/google_server_show.rb +44 -0
  23. data/lib/chef/knife/google_zone_list.rb +39 -50
  24. data/lib/knife-google/version.rb +3 -2
  25. data/spec/cloud/google_service_helpers_spec.rb +120 -0
  26. data/spec/cloud/google_service_spec.rb +832 -0
  27. data/spec/google_disk_create_spec.rb +72 -0
  28. data/spec/google_disk_delete_spec.rb +64 -0
  29. data/spec/google_disk_list_spec.rb +93 -0
  30. data/spec/google_project_quotas_spec.rb +63 -0
  31. data/spec/google_region_list_spec.rb +65 -0
  32. data/spec/google_region_quotas_spec.rb +108 -0
  33. data/spec/google_server_create_spec.rb +177 -0
  34. data/spec/google_server_delete_spec.rb +39 -0
  35. data/spec/google_server_list_spec.rb +77 -0
  36. data/spec/google_server_show_spec.rb +60 -0
  37. data/spec/google_zone_list_spec.rb +59 -0
  38. metadata +91 -114
  39. data/CONTRIB.md +0 -64
  40. data/lib/chef/knife/google_base.rb +0 -76
  41. data/lib/chef/knife/google_project_list.rb +0 -178
  42. data/lib/chef/knife/google_setup.rb +0 -31
  43. data/lib/google/compute.rb +0 -47
  44. data/lib/google/compute/client.rb +0 -216
  45. data/lib/google/compute/config.rb +0 -23
  46. data/lib/google/compute/creatable_resource_collection.rb +0 -55
  47. data/lib/google/compute/deletable_resource_collection.rb +0 -51
  48. data/lib/google/compute/disk.rb +0 -38
  49. data/lib/google/compute/exception.rb +0 -30
  50. data/lib/google/compute/firewall.rb +0 -65
  51. data/lib/google/compute/global_operation.rb +0 -60
  52. data/lib/google/compute/image.rb +0 -29
  53. data/lib/google/compute/listable_resource_collection.rb +0 -33
  54. data/lib/google/compute/machine_type.rb +0 -36
  55. data/lib/google/compute/mixins/utils.rb +0 -58
  56. data/lib/google/compute/network.rb +0 -29
  57. data/lib/google/compute/project.rb +0 -76
  58. data/lib/google/compute/region.rb +0 -31
  59. data/lib/google/compute/region_operation.rb +0 -62
  60. data/lib/google/compute/resource.rb +0 -81
  61. data/lib/google/compute/resource_collection.rb +0 -78
  62. data/lib/google/compute/server.rb +0 -88
  63. data/lib/google/compute/server/attached_disk.rb +0 -39
  64. data/lib/google/compute/server/network_interface.rb +0 -38
  65. data/lib/google/compute/server/network_interface/access_config.rb +0 -35
  66. data/lib/google/compute/server/serial_port_output.rb +0 -31
  67. data/lib/google/compute/snapshot.rb +0 -30
  68. data/lib/google/compute/version.rb +0 -19
  69. data/lib/google/compute/zone.rb +0 -34
  70. data/lib/google/compute/zone_operation.rb +0 -62
  71. data/spec/chef/knife/google_base_spec.rb +0 -46
  72. data/spec/chef/knife/google_disk_create_spec.rb +0 -37
  73. data/spec/chef/knife/google_disk_delete_spec.rb +0 -64
  74. data/spec/chef/knife/google_disk_list_spec.rb +0 -36
  75. data/spec/chef/knife/google_region_list_spec.rb +0 -32
  76. data/spec/chef/knife/google_server_create_spec.rb +0 -138
  77. data/spec/chef/knife/google_server_delete_spec.rb +0 -127
  78. data/spec/chef/knife/google_server_list_spec.rb +0 -39
  79. data/spec/chef/knife/google_setup_spec.rb +0 -24
  80. data/spec/chef/knife/google_zone_list_spec.rb +0 -32
  81. data/spec/data/client.json +0 -14
  82. data/spec/data/compute-v1.json +0 -6734
  83. data/spec/data/disk.json +0 -14
  84. data/spec/data/firewall.json +0 -13
  85. data/spec/data/global_operation.json +0 -36
  86. data/spec/data/image.json +0 -12
  87. data/spec/data/machine_type.json +0 -24
  88. data/spec/data/network.json +0 -10
  89. data/spec/data/project.json +0 -21
  90. data/spec/data/region.json +0 -23
  91. data/spec/data/serial_port_output.json +0 -5
  92. data/spec/data/server.json +0 -46
  93. data/spec/data/snapshot.json +0 -12
  94. data/spec/data/zone.json +0 -22
  95. data/spec/data/zone_operation.json +0 -36
  96. data/spec/google/compute/disk_spec.rb +0 -115
  97. data/spec/google/compute/firewall_spec.rb +0 -129
  98. data/spec/google/compute/global_operation_spec.rb +0 -62
  99. data/spec/google/compute/image_spec.rb +0 -75
  100. data/spec/google/compute/machine_type_spec.rb +0 -53
  101. data/spec/google/compute/network_spec.rb +0 -68
  102. data/spec/google/compute/project_spec.rb +0 -71
  103. data/spec/google/compute/region_spec.rb +0 -51
  104. data/spec/google/compute/server_spec.rb +0 -118
  105. data/spec/google/compute/snapshot_spec.rb +0 -57
  106. data/spec/google/compute/zone_operation_spec.rb +0 -62
  107. data/spec/google/compute/zone_spec.rb +0 -51
  108. data/spec/spec_helper.rb +0 -45
  109. data/spec/support/mocks.rb +0 -62
  110. data/spec/support/resource_examples.rb +0 -70
  111. data/spec/support/spec_google_base.rb +0 -60
@@ -0,0 +1,72 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2016 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "chef/knife/google_disk_create"
21
+ require "support/shared_examples_for_command"
22
+
23
+ describe Chef::Knife::Cloud::GoogleDiskCreate do
24
+ let(:command) { described_class.new(%w{disk1}) }
25
+ let(:service) { double("service") }
26
+
27
+ before do
28
+ allow(command).to receive(:service).and_return(service)
29
+ end
30
+
31
+ it_behaves_like Chef::Knife::Cloud::Command, described_class.new
32
+
33
+ describe '#validate_params!' do
34
+ before do
35
+ allow(command).to receive(:check_for_missing_config_values!)
36
+ allow(command).to receive(:valid_disk_size?).and_return(true)
37
+ end
38
+
39
+ it "checks for missing config values" do
40
+ expect(command).to receive(:check_for_missing_config_values!).with(:disk_size, :disk_type)
41
+
42
+ command.validate_params!
43
+ end
44
+
45
+ it "does not raise an exception if all params are good" do
46
+ expect { command.validate_params! }.not_to raise_error
47
+ end
48
+
49
+ it "raises an exception if the disk size is invalid" do
50
+ expect(command).to receive(:valid_disk_size?).and_return(false)
51
+ expect { command.validate_params! }.to raise_error(RuntimeError, "Disk size must be between 10 and 10,000")
52
+ end
53
+
54
+ context "when no disk name is provided" do
55
+ let(:command) { described_class.new }
56
+ it "raises an exception" do
57
+ expect { command.validate_params! }.to raise_error(RuntimeError, "Please specify a disk name.")
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#execute_command' do
63
+ it "calls the service to create the disk" do
64
+ expect(command).to receive(:locate_config_value).with(:disk_size).and_return("size")
65
+ expect(command).to receive(:locate_config_value).with(:disk_type).and_return("type")
66
+ expect(command).to receive(:locate_config_value).with(:disk_source).and_return("source")
67
+ expect(service).to receive(:create_disk).with("disk1", "size", "type", "source")
68
+
69
+ command.execute_command
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,64 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2016 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "chef/knife/google_disk_delete"
21
+ require "support/shared_examples_for_command"
22
+
23
+ describe Chef::Knife::Cloud::GoogleDiskDelete do
24
+ let(:command) { described_class.new(%w{disk1 disk2}) }
25
+ let(:service) { double("service") }
26
+
27
+ before do
28
+ allow(command).to receive(:service).and_return(service)
29
+ end
30
+
31
+ it_behaves_like Chef::Knife::Cloud::Command, described_class.new
32
+
33
+ describe '#validate_params!' do
34
+ before do
35
+ allow(command).to receive(:check_for_missing_config_values!)
36
+ end
37
+
38
+ it "checks for missing config values" do
39
+ expect(command).to receive(:check_for_missing_config_values!)
40
+
41
+ command.validate_params!
42
+ end
43
+
44
+ it "does not raise an exception if all params are good" do
45
+ expect { command.validate_params! }.not_to raise_error
46
+ end
47
+
48
+ context "when no disk name is provided" do
49
+ let(:command) { described_class.new }
50
+ it "raises an exception" do
51
+ expect { command.validate_params! }.to raise_error(RuntimeError, "You must specify at least one disk to delete.")
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#execute_command' do
57
+ it "calls the service to delete each disk" do
58
+ expect(service).to receive(:delete_disk).with("disk1")
59
+ expect(service).to receive(:delete_disk).with("disk2")
60
+
61
+ command.execute_command
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,93 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2016 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "chef/knife/google_disk_list"
21
+ require "support/shared_examples_for_command"
22
+
23
+ describe Chef::Knife::Cloud::GoogleDiskList do
24
+ let(:command) { described_class.new }
25
+ let(:service) { double("service") }
26
+
27
+ before do
28
+ allow(command).to receive(:service).and_return(service)
29
+ end
30
+
31
+ it_behaves_like Chef::Knife::Cloud::Command, described_class.new
32
+
33
+ describe '#validate_params!' do
34
+ it "checks for missing config values" do
35
+ expect(command).to receive(:check_for_missing_config_values!)
36
+
37
+ command.validate_params!
38
+ end
39
+ end
40
+
41
+ describe '#query_resource' do
42
+ it "uses the service to list disks" do
43
+ expect(service).to receive(:list_disks).and_return("disks")
44
+ expect(command.query_resource).to eq("disks")
45
+ end
46
+ end
47
+
48
+ describe '#format_status_value' do
49
+ it "returns green when the status is ready" do
50
+ expect(command.ui).to receive(:color).with("ready", :green)
51
+ command.format_status_value("ready")
52
+ end
53
+
54
+ it "returns red when the status is stopped" do
55
+ expect(command.ui).to receive(:color).with("stopped", :red)
56
+ command.format_status_value("stopped")
57
+ end
58
+ end
59
+
60
+ describe '#format_disk_type' do
61
+ it "returns a properly-formatted disk type" do
62
+ expect(command.format_disk_type("a/b/c/disk_type")).to eq("disk_type")
63
+ end
64
+ end
65
+
66
+ describe '#format_source_image' do
67
+ it "returns 'unknown' if the source is nil" do
68
+ expect(command.format_source_image(nil)).to eq("unknown")
69
+ end
70
+
71
+ it "returns 'unknown' if the source is empty" do
72
+ expect(command.format_source_image([])).to eq("unknown")
73
+ end
74
+
75
+ it "returns a properly-formatted image URL" do
76
+ expect(command.format_source_image("/1/2/3/4/5/6/image_name")).to eq("4/5/6/image_name")
77
+ end
78
+ end
79
+
80
+ describe '#format_users' do
81
+ it "returns 'unknown' if the source is nil" do
82
+ expect(command.format_users(nil)).to eq("none")
83
+ end
84
+
85
+ it "returns 'unknown' if the source is empty" do
86
+ expect(command.format_users([])).to eq("none")
87
+ end
88
+
89
+ it "returns a properly-formatted user URL" do
90
+ expect(command.format_users(["/1/2/3/4/5/6/user1", "/1/2/3/4/5/6/user2"])).to eq("3/4/5/6/user1, 3/4/5/6/user2")
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,63 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2016 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "chef/knife/google_project_quotas"
21
+ require "support/shared_examples_for_command"
22
+
23
+ describe Chef::Knife::Cloud::GoogleProjectQuotas do
24
+ let(:command) { described_class.new }
25
+ let(:service) { double("service") }
26
+
27
+ before do
28
+ allow(command).to receive(:service).and_return(service)
29
+ end
30
+
31
+ it_behaves_like Chef::Knife::Cloud::Command, described_class.new
32
+
33
+ describe '#validate_params!' do
34
+ it "checks for missing config values" do
35
+ expect(command).to receive(:check_for_missing_config_values!)
36
+
37
+ command.validate_params!
38
+ end
39
+ end
40
+
41
+ describe '#query_resource' do
42
+ it "uses the service to list project quotas" do
43
+ expect(service).to receive(:list_project_quotas).and_return("quotas")
44
+ expect(command.query_resource).to eq("quotas")
45
+ end
46
+ end
47
+
48
+ describe '#format_name' do
49
+ it "returns a properly-formatted name" do
50
+ expect(command.format_name("something_cool_here")).to eq("Something Cool Here")
51
+ end
52
+ end
53
+
54
+ describe '#format_number' do
55
+ it "returns an integer as a string if the number is a whole number" do
56
+ expect(command.format_number(2.0)).to eq("2")
57
+ end
58
+
59
+ it "returns the number as-is if it is not a whole number" do
60
+ expect(command.format_number(2.5)).to eq("2.5")
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,65 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2016 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "chef/knife/google_region_list"
21
+ require "support/shared_examples_for_command"
22
+
23
+ describe Chef::Knife::Cloud::GoogleRegionList do
24
+ let(:command) { described_class.new }
25
+ let(:service) { double("service") }
26
+
27
+ before do
28
+ allow(command).to receive(:service).and_return(service)
29
+ end
30
+
31
+ it_behaves_like Chef::Knife::Cloud::Command, described_class.new
32
+
33
+ describe '#validate_params!' do
34
+ it "checks for missing config values" do
35
+ expect(command).to receive(:check_for_missing_config_values!)
36
+
37
+ command.validate_params!
38
+ end
39
+ end
40
+
41
+ describe '#query_resource' do
42
+ it "uses the service to list regions" do
43
+ expect(service).to receive(:list_regions).and_return("regions")
44
+ expect(command.query_resource).to eq("regions")
45
+ end
46
+ end
47
+
48
+ describe '#format_status_value' do
49
+ it "returns green when the status is up" do
50
+ expect(command.ui).to receive(:color).with("up", :green)
51
+ command.format_status_value("up")
52
+ end
53
+
54
+ it "returns red when the status is stopped" do
55
+ expect(command.ui).to receive(:color).with("stopped", :red)
56
+ command.format_status_value("stopped")
57
+ end
58
+ end
59
+
60
+ describe '#format_zones' do
61
+ it "returns properly-formatted zones" do
62
+ expect(command.format_zones(["a/b/zone1", "c/d/zone2"])).to eq("zone1, zone2")
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,108 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2016 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "chef/knife/google_region_quotas"
21
+ require "support/shared_examples_for_command"
22
+
23
+ describe Chef::Knife::Cloud::GoogleRegionQuotas do
24
+ let(:command) { described_class.new }
25
+ let(:service) { double("service") }
26
+
27
+ before do
28
+ allow(command).to receive(:service).and_return(service)
29
+ end
30
+
31
+ it_behaves_like Chef::Knife::Cloud::Command, described_class.new
32
+
33
+ describe '#validate_params!' do
34
+ it "checks for missing config values" do
35
+ expect(command).to receive(:check_for_missing_config_values!)
36
+
37
+ command.validate_params!
38
+ end
39
+ end
40
+
41
+ describe '#execute_command' do
42
+ let(:ui) { double("ui") }
43
+ let(:regions) { [region1] }
44
+
45
+ before do
46
+ allow(command).to receive(:ui).and_return(ui)
47
+ allow(ui).to receive(:msg)
48
+ allow(ui).to receive(:color)
49
+ allow(ui).to receive(:list)
50
+ expect(service).to receive(:list_regions).and_return(regions)
51
+ end
52
+
53
+ context "when the quota information for the region is nil" do
54
+ let(:region1) { double("region1", name: "my-region", quotas: nil) }
55
+
56
+ it "prints a warning and does not output a list" do
57
+ expect(ui).to receive(:warn).with("No quota information available for this region.")
58
+ expect(ui).not_to receive(:list)
59
+ command.execute_command
60
+ end
61
+ end
62
+
63
+ context "when the quota information for the region is empty" do
64
+ let(:region1) { double("region1", name: "my-region", quotas: []) }
65
+
66
+ it "prints a warning and does not output a list" do
67
+ expect(ui).to receive(:warn).with("No quota information available for this region.")
68
+ expect(ui).not_to receive(:list)
69
+ command.execute_command
70
+ end
71
+ end
72
+
73
+ context "when there is quota information available" do
74
+ let(:quota1) { double("quota1", metric: "metric1", limit: "limit1", usage: "usage1") }
75
+ let(:quota2) { double("quota2", metric: "metric2", limit: "limit2", usage: "usage2") }
76
+ let(:region1) { double("region1", name: "my-region", quotas: [quota1, quota2]) }
77
+
78
+ it "formats the output and outputs a list" do
79
+ expect(command).to receive(:format_name).with("metric1")
80
+ expect(command).to receive(:format_name).with("metric2")
81
+ expect(command).to receive(:format_number).with("limit1")
82
+ expect(command).to receive(:format_number).with("limit2")
83
+ expect(command).to receive(:format_number).with("usage1")
84
+ expect(command).to receive(:format_number).with("usage2")
85
+ expect(ui).to receive(:list).and_return("list_output")
86
+ expect(ui).to receive(:msg).with("list_output")
87
+
88
+ command.execute_command
89
+ end
90
+ end
91
+ end
92
+
93
+ describe '#format_name' do
94
+ it "returns a properly-formatted name" do
95
+ expect(command.format_name("something_cool_here")).to eq("Something Cool Here")
96
+ end
97
+ end
98
+
99
+ describe '#format_number' do
100
+ it "returns an integer as a string if the number is a whole number" do
101
+ expect(command.format_number(2.0)).to eq("2")
102
+ end
103
+
104
+ it "returns the number as-is if it is not a whole number" do
105
+ expect(command.format_number(2.5)).to eq("2.5")
106
+ end
107
+ end
108
+ end