gradesfirst 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -6
- data/gradesfirst.gemspec +2 -2
- data/lib/gradesfirst.rb +1 -0
- data/lib/gradesfirst/cli.rb +7 -29
- data/lib/gradesfirst/cli_helper.rb +33 -0
- data/lib/gradesfirst/command.rb +7 -0
- data/lib/gradesfirst/commit_message_command.rb +1 -1
- data/lib/gradesfirst/task_add_command.rb +51 -0
- data/lib/gradesfirst/task_cli.rb +48 -0
- data/lib/gradesfirst/task_command.rb +38 -30
- data/lib/gradesfirst/task_delete_command.rb +64 -0
- data/lib/gradesfirst/task_list_command.rb +40 -0
- data/lib/gradesfirst/task_move_command.rb +64 -0
- data/lib/gradesfirst/task_toggle_command.rb +65 -0
- data/lib/http_magic.rb +2 -258
- data/lib/http_magic/api.rb +233 -0
- data/lib/http_magic/request.rb +93 -0
- data/lib/http_magic/uri.rb +74 -0
- data/lib/pivotal_tracker.rb +1 -1
- data/test/branch_command_test.rb +8 -21
- data/test/cli_test.rb +51 -34
- data/test/command_test.rb +5 -7
- data/test/commit_message_command_test.rb +37 -45
- data/test/fixtures/task.json +10 -0
- data/test/fixtures/task_added.txt +6 -0
- data/test/fixtures/task_deleted.txt +6 -0
- data/test/fixtures/task_moved.txt +6 -0
- data/test/fixtures/task_toggled.txt +6 -0
- data/test/fixtures/tasks.txt +2 -2
- data/test/http_magic/{get_test.rb → api/get_test.rb} +1 -1
- data/test/http_magic/api/post_test.rb +34 -0
- data/test/http_magic/request_test.rb +63 -0
- data/test/http_magic/uri_test.rb +28 -55
- data/test/support/pivotal_test_helper.rb +110 -0
- data/test/support/request_expectation.rb +33 -0
- data/test/task_add_command_test.rb +54 -0
- data/test/task_delete_command_test.rb +57 -0
- data/test/task_list_command_test.rb +18 -0
- data/test/task_move_command_test.rb +66 -0
- data/test/task_toggle_command_test.rb +58 -0
- data/test/test_helper.rb +35 -19
- metadata +29 -7
- data/test/pivotal_tracker_test.rb +0 -46
- data/test/task_command_test.rb +0 -52
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module HttpMagic
|
5
|
+
# Encapsulating class to hold all of the infrastructure to make the actual
|
6
|
+
# requests to the api. It receives at a minimum a HttpMagic::Uri object which
|
7
|
+
# manages the url building.
|
8
|
+
#
|
9
|
+
# == Example
|
10
|
+
#
|
11
|
+
# uri_object = HttpMagic::Uri.new('http://example.com')
|
12
|
+
#
|
13
|
+
# request = Request.new(uri_object)
|
14
|
+
#
|
15
|
+
# request.get
|
16
|
+
# => { 'name' => 'Foo Bar' }
|
17
|
+
class Request
|
18
|
+
def initialize(uri, options = {})
|
19
|
+
@uri = uri
|
20
|
+
@data = options[:data]
|
21
|
+
@options = {
|
22
|
+
headers: options[:headers] || {}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete
|
27
|
+
parse_response http.delete(@uri.urn, @options[:headers])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Makes a GET request to the url provided by the Uri object and returns the
|
31
|
+
# resulting JSON data as a Ruby hash.
|
32
|
+
#
|
33
|
+
# == Example
|
34
|
+
#
|
35
|
+
# uri_object = HttpMagic::Uri.new('http://example.com')
|
36
|
+
#
|
37
|
+
# request = Request.new(uri_object)
|
38
|
+
#
|
39
|
+
# request.get
|
40
|
+
# => { 'name' => 'Foo Bar' }
|
41
|
+
def get
|
42
|
+
parse_response http.request_get(@uri.urn, @options[:headers])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Makes a POST request to the url provided by the Uri object and returns the
|
46
|
+
# resulting JSON data as a Ruby hash. If data was provided as an optional
|
47
|
+
# initialization parameter, then that is also POSTed.
|
48
|
+
#
|
49
|
+
# == Example
|
50
|
+
#
|
51
|
+
# uri_object = HttpMagic::Uri.new('http://example.com')
|
52
|
+
#
|
53
|
+
# request = Request.new(uri_object, data: { name: 'New Foo' })
|
54
|
+
#
|
55
|
+
# request.post
|
56
|
+
def post
|
57
|
+
if !@data.empty?
|
58
|
+
@options[:headers].merge!( 'Content-Type' => 'application/json' )
|
59
|
+
end
|
60
|
+
|
61
|
+
parse_response http.request_post(@uri.urn, @data.to_json, @options[:headers])
|
62
|
+
end
|
63
|
+
|
64
|
+
def put
|
65
|
+
if !@data.empty?
|
66
|
+
@options[:headers].merge!( 'Content-Type' => 'application/json' )
|
67
|
+
end
|
68
|
+
|
69
|
+
parse_response http.request_put(@uri.urn, @data.to_json, @options[:headers])
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def http
|
75
|
+
return @http unless @http.nil?
|
76
|
+
@http = Net::HTTP.new(@uri.domain, 443)
|
77
|
+
@http.use_ssl = true
|
78
|
+
@http
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_response(response)
|
82
|
+
if response && response.is_a?(Net::HTTPSuccess)
|
83
|
+
if response.body && response.content_type == 'application/json'
|
84
|
+
JSON.parse(response.body)
|
85
|
+
else
|
86
|
+
response.body.to_s
|
87
|
+
end
|
88
|
+
else
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module HttpMagic
|
3
|
+
# Helper class that holds the parts of the URI and provides methods to put
|
4
|
+
# them together.
|
5
|
+
#
|
6
|
+
# == Example
|
7
|
+
#
|
8
|
+
# uri = HttpMagic::Uri.new('example.com')
|
9
|
+
# uri.build
|
10
|
+
# => "https://example.com/"
|
11
|
+
#
|
12
|
+
# uri.namespace = 'api/v2'
|
13
|
+
# uri.build
|
14
|
+
# => "https://example.com/api/v2"
|
15
|
+
#
|
16
|
+
# uri.parts = ['path']
|
17
|
+
# uri.build
|
18
|
+
# => "https://example.com/api/v2/path"
|
19
|
+
#
|
20
|
+
class Uri
|
21
|
+
attr_accessor :namespace, :parts
|
22
|
+
|
23
|
+
attr_reader :domain
|
24
|
+
|
25
|
+
def initialize(domain)
|
26
|
+
@domain = domain
|
27
|
+
@parts = []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Builds a full uniform resource identifier.
|
31
|
+
#
|
32
|
+
# == Example
|
33
|
+
#
|
34
|
+
# uri = HttpMagic::Uri.new('example.com')
|
35
|
+
# uri.namespace = 'api/v1'
|
36
|
+
# uri.parts = %w(foo bar)
|
37
|
+
#
|
38
|
+
# uri.urn
|
39
|
+
# => "https://example.com/api/v1/foo/bar"
|
40
|
+
def build
|
41
|
+
"#{url}#{urn}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Uniform resource locator based on @domain value.
|
45
|
+
#
|
46
|
+
# == Example
|
47
|
+
#
|
48
|
+
# uri = HttpMagic::Uri.new('example.com')
|
49
|
+
#
|
50
|
+
# uri.url
|
51
|
+
# => "https://example.com/"
|
52
|
+
#
|
53
|
+
# uri.url(false)
|
54
|
+
# => "https://example.com"
|
55
|
+
def url
|
56
|
+
"https://#{@domain}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Uniform resource name for a resource.
|
60
|
+
#
|
61
|
+
# == Example
|
62
|
+
#
|
63
|
+
# uri = HttpMagic::Uri.new('example.com')
|
64
|
+
# uri.namespace = 'api/v1'
|
65
|
+
# uri.parts = %w(foo bar)
|
66
|
+
#
|
67
|
+
# uri.urn
|
68
|
+
# => "/api/v1/foo/bar"
|
69
|
+
def urn
|
70
|
+
resource_name = [@namespace, @parts].flatten.compact.join('/')
|
71
|
+
"/#{resource_name}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/pivotal_tracker.rb
CHANGED
data/test/branch_command_test.rb
CHANGED
@@ -1,32 +1,19 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require 'pivotal_tracker'
|
2
3
|
require 'gradesfirst/branch_command'
|
4
|
+
require 'pivotal_tracker'
|
3
5
|
|
4
6
|
describe GradesFirst::BranchCommand do
|
5
7
|
before do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
)
|
11
|
-
|
12
|
-
stub_pivotal_request(
|
13
|
-
:get,
|
14
|
-
'stories/57348714',
|
15
|
-
body: fixture_file('story_foo.json')
|
16
|
-
)
|
17
|
-
|
18
|
-
PivotalTracker.api_token = 'test_token'
|
19
|
-
git_branch_output = fixture_file('git_branch.txt')
|
20
|
-
@command = GradesFirst::BranchCommand.new
|
21
|
-
@command.stub :git_branch, git_branch_output do
|
22
|
-
@command.execute
|
8
|
+
expect_requests stub_bar_story_request, stub_foo_story_request do
|
9
|
+
git_branch_output = fixture_file('git_branch.txt')
|
10
|
+
@command = GradesFirst::BranchCommand.new
|
11
|
+
@command.stub :git_branch, git_branch_output, &call_execute
|
23
12
|
end
|
24
13
|
end
|
25
14
|
|
26
|
-
|
27
|
-
|
28
|
-
assert_equal fixture_file('gf_branch.txt'), @command.response
|
29
|
-
end
|
15
|
+
specify '#response responds with Pivotal enhancements' do
|
16
|
+
assert_equal fixture_file('gf_branch.txt'), @command.response
|
30
17
|
end
|
31
18
|
|
32
19
|
end
|
data/test/cli_test.rb
CHANGED
@@ -2,52 +2,69 @@ require 'test_helper'
|
|
2
2
|
require 'gradesfirst/cli'
|
3
3
|
|
4
4
|
describe GradesFirst::CLI do
|
5
|
-
def command_not_found(command)
|
6
|
-
"Could not find task \"#{command}\".\n"
|
7
|
-
end
|
8
5
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
end
|
6
|
+
def assert_command_hooked_up(shell_command, klass)
|
7
|
+
assert_includes klass.instance_methods, :execute
|
8
|
+
assert_includes klass.instance_methods, :response
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
execute_arity = klass.instance_method(:execute).arity
|
11
|
+
if execute_arity < 0
|
12
|
+
execute_arity += 1
|
13
|
+
execute_arity = execute_arity.abs
|
17
14
|
end
|
15
|
+
expected_execute_parameters = Array.new(execute_arity, Object)
|
16
|
+
|
17
|
+
expected_output = 'Expected Response'
|
18
|
+
command_instance = Minitest::Mock.new.
|
19
|
+
expect(:execute, nil, expected_execute_parameters).
|
20
|
+
expect(:response, expected_output)
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
GradesFirst::CLI.start
|
22
|
+
stdout, stderr = capture_io do
|
23
|
+
klass.stub :new, command_instance do
|
24
|
+
GradesFirst::CLI.start Shellwords.split(shell_command)
|
22
25
|
end
|
23
|
-
refute_includes output, command_not_found('branch')
|
24
26
|
end
|
27
|
+
|
28
|
+
assert_equal '', stderr, "Error reported to standard error by CLI command \"#{shell_command}\""
|
29
|
+
assert_match expected_output, stdout, "Incorrect output from CLI command \"#{shell_command}\""
|
30
|
+
command_instance.verify
|
25
31
|
end
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
33
|
+
specify 'branch command is hooked up' do
|
34
|
+
assert_command_hooked_up 'branch', GradesFirst::BranchCommand
|
35
|
+
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
GradesFirst::CLI.start %w{ commit-message }
|
35
|
-
end
|
36
|
-
refute_includes output, command_not_found('commit-message')
|
37
|
-
end
|
37
|
+
specify 'commit-message command is hooked up' do
|
38
|
+
assert_command_hooked_up 'commit-message', GradesFirst::CommitMessageCommand
|
38
39
|
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
# Waiting on fix for subcommands with default command.
|
42
|
+
# https://github.com/erikhuda/thor/pull/374
|
43
|
+
#specify 'task command is hooked up to default list subcommand' do
|
44
|
+
# assert_command_hooked_up 'task', GradesFirst::TaskListCommand
|
45
|
+
#end
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
specify 'task add command is hooked up' do
|
48
|
+
assert_command_hooked_up(
|
49
|
+
'task add "This is a task."',
|
50
|
+
GradesFirst::TaskAddCommand
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
specify 'task delete command is hooked up' do
|
55
|
+
assert_command_hooked_up('task delete 1', GradesFirst::TaskDeleteCommand)
|
56
|
+
end
|
57
|
+
|
58
|
+
specify 'task list command is hooked up' do
|
59
|
+
assert_command_hooked_up 'task list', GradesFirst::TaskListCommand
|
60
|
+
end
|
61
|
+
|
62
|
+
specify 'task move command is hooked up' do
|
63
|
+
assert_command_hooked_up('task move 5 1', GradesFirst::TaskMoveCommand)
|
64
|
+
end
|
65
|
+
|
66
|
+
specify 'task toggle command is hooked up' do
|
67
|
+
assert_command_hooked_up('task toggle 1', GradesFirst::TaskToggleCommand)
|
51
68
|
end
|
52
69
|
|
53
70
|
end
|
data/test/command_test.rb
CHANGED
@@ -2,13 +2,11 @@ require 'test_helper'
|
|
2
2
|
require 'gradesfirst/command'
|
3
3
|
|
4
4
|
describe GradesFirst::Command do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
5
|
+
specify '#story_id pulls the story id from the branch' do
|
6
|
+
assert '123', GradesFirst::Command.new.send(:story_id, 'abc/123')
|
7
|
+
end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
specify '#story_id returns nil if not story id' do
|
10
|
+
assert_nil GradesFirst::Command.new.send(:story_id, 'abc')
|
13
11
|
end
|
14
12
|
end
|
@@ -1,62 +1,54 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require 'pivotal_tracker'
|
2
3
|
require 'gradesfirst/commit_message_command'
|
4
|
+
require 'pivotal_tracker'
|
3
5
|
|
4
6
|
describe GradesFirst::CommitMessageCommand do
|
5
|
-
before do
|
6
|
-
stub_pivotal_request(
|
7
|
-
:get,
|
8
|
-
'stories/29384793',
|
9
|
-
body: fixture_file('story_bar.json')
|
10
|
-
)
|
11
|
-
|
12
|
-
PivotalTracker.api_token = 'test_token'
|
13
|
-
end
|
14
|
-
|
15
7
|
describe '#response' do
|
16
|
-
it '
|
8
|
+
it 'responds with formatted commit message' do
|
17
9
|
command = GradesFirst::CommitMessageCommand.new
|
18
|
-
|
19
|
-
command.
|
20
|
-
assert_equal fixture_file('commit_message.txt'), command.response
|
10
|
+
expect_requests stub_bar_story_request do
|
11
|
+
command.stub :current_branch, "test/#{bar_story_id}", &call_execute
|
21
12
|
end
|
13
|
+
assert_equal fixture_file('commit_message.txt'), command.response
|
22
14
|
end
|
23
15
|
end
|
24
|
-
end
|
25
16
|
|
26
|
-
describe '
|
27
|
-
|
28
|
-
|
29
|
-
|
17
|
+
describe '#constrain_line_length' do
|
18
|
+
def test_string
|
19
|
+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vitae.'
|
20
|
+
end
|
30
21
|
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
def test_string_ending_with_a_letter
|
23
|
+
'Lorem ipsum dolor sit amet'
|
24
|
+
end
|
34
25
|
|
35
|
-
|
36
|
-
|
37
|
-
|
26
|
+
def test_string_with_line_feeds_at_30
|
27
|
+
"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.\nFusce vitae."
|
28
|
+
end
|
38
29
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
30
|
+
it 'adds new lines at natural breaks when length is exceeded' do
|
31
|
+
command = GradesFirst::CommitMessageCommand.new
|
32
|
+
assert_equal(
|
33
|
+
test_string_with_line_feeds_at_30,
|
34
|
+
command.send(:constrain_line_length, test_string, 30)
|
35
|
+
)
|
36
|
+
end
|
46
37
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
38
|
+
it 'does nothing when length is not exceeded' do
|
39
|
+
command = GradesFirst::CommitMessageCommand.new
|
40
|
+
assert_equal(
|
41
|
+
test_string,
|
42
|
+
command.send(:constrain_line_length, test_string, 80)
|
43
|
+
)
|
44
|
+
end
|
54
45
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
46
|
+
it 'does not wrap when ending with a letter' do
|
47
|
+
command = GradesFirst::CommitMessageCommand.new
|
48
|
+
assert_equal(
|
49
|
+
test_string_ending_with_a_letter,
|
50
|
+
command.send(:constrain_line_length, test_string_ending_with_a_letter, 80)
|
51
|
+
)
|
52
|
+
end
|
61
53
|
end
|
62
54
|
end
|