提交 a6318c02 编写于 作者: M Mislav Marohnić

Merge remote-tracking branch 'origin/master' into bugfix/remote-ssh-protocol-not-own-repo

......@@ -88,15 +88,13 @@ func ciStatus(cmd *Command, args *Args) {
response, err := gh.FetchCIStatus(project, sha)
utils.Check(err)
state := response.State
state := ""
if len(response.Statuses) > 0 {
for _, status := range response.Statuses {
if checkSeverity(status.State) > checkSeverity(state) {
state = status.State
}
}
} else if len(response.Statuses) == 0 {
state = ""
}
var exitCode int
......
......@@ -72,14 +72,14 @@ func compare(command *Command, args *Args) {
r string
)
branch, project, err = localRepo.RemoteBranchAndProject("", false)
utils.Check(err)
usageHelp := func() {
utils.Check(fmt.Errorf("Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]"))
}
if args.IsParamsEmpty() {
branch, project, err = localRepo.RemoteBranchAndProject("", false)
utils.Check(err)
if branch == nil ||
(branch.IsMaster() && flagCompareBase == "") ||
(flagCompareBase == branch.ShortName()) {
......
......@@ -17,6 +17,9 @@ var cmdFork = &Command{
--no-remote
Skip adding a git remote for the fork.
--remote-name=<REMOTE>
Set the name for the new git remote.
--org=<ORGANIZATION>
Fork the repository within this organization.
......
......@@ -18,6 +18,7 @@ var (
Run: listIssues,
Usage: `
issue [-a <ASSIGNEE>] [-c <CREATOR>] [-@ <USER>] [-s <STATE>] [-f <FORMAT>] [-M <MILESTONE>] [-l <LABELS>] [-d <DATE>] [-o <SORT_KEY> [-^]] [-L <LIMIT>]
issue show [-f <FORMAT>] <NUMBER>
issue create [-oc] [-m <MESSAGE>|-F <FILE>] [--edit] [-a <USERS>] [-M <MILESTONE>] [-l <LABELS>]
issue labels [--color]
`,
......@@ -27,6 +28,9 @@ issue labels [--color]
With no arguments, show a list of open issues.
* _show_:
Show an existing issue specified by <NUMBER>.
* _create_:
Open an issue in the current project.
......@@ -153,6 +157,13 @@ With no arguments, show a list of open issues.
Long: "Open an issue in the current project.",
}
cmdShowIssue = &Command{
Key: "show",
Run: showIssue,
Usage: "issue show <NUMBER>",
Long: "Show an issue in the current project.",
}
cmdLabel = &Command{
Key: "labels",
Run: listLabels,
......@@ -163,6 +174,7 @@ With no arguments, show a list of open issues.
flagIssueAssignee,
flagIssueState,
flagIssueFormat,
flagShowIssueFormat,
flagIssueMessage,
flagIssueMilestoneFilter,
flagIssueCreator,
......@@ -189,6 +201,8 @@ With no arguments, show a list of open issues.
)
func init() {
cmdShowIssue.Flag.StringVarP(&flagShowIssueFormat, "format", "f", "", "FORMAT")
cmdCreateIssue.Flag.StringVarP(&flagIssueMessage, "message", "m", "", "MESSAGE")
cmdCreateIssue.Flag.StringVarP(&flagIssueFile, "file", "F", "", "FILE")
cmdCreateIssue.Flag.Uint64VarP(&flagIssueMilestone, "milestone", "M", 0, "MILESTONE")
......@@ -213,6 +227,7 @@ func init() {
cmdLabel.Flag.BoolVarP(&flagLabelsColorize, "color", "", false, "COLORIZE")
cmdIssue.Use(cmdShowIssue)
cmdIssue.Use(cmdCreateIssue)
cmdIssue.Use(cmdLabel)
CmdRunner.Use(cmdIssue)
......@@ -248,6 +263,8 @@ func listIssues(cmd *Command, args *Args) {
if flagIssueSortAscending {
filters["direction"] = "asc"
} else {
filters["direction"] = "desc"
}
if cmd.FlagPassed("since") {
......@@ -310,6 +327,15 @@ func formatIssuePlaceholders(issue github.Issue, colorize bool) map[string]strin
assignees = append(assignees, assignee.Login)
}
var requestedReviewers []string
for _, requestedReviewer := range issue.RequestedReviewers {
requestedReviewers = append(requestedReviewers, requestedReviewer.Login)
}
for _, requestedTeam := range issue.RequestedTeams {
teamSlug := fmt.Sprintf("%s/%s", issue.Base.Repo.Owner.Login, requestedTeam.Slug)
requestedReviewers = append(requestedReviewers, teamSlug)
}
var milestoneNumber, milestoneTitle string
if issue.Milestone != nil {
milestoneNumber = fmt.Sprintf("%d", issue.Milestone.Number)
......@@ -349,6 +375,7 @@ func formatIssuePlaceholders(issue github.Issue, colorize bool) map[string]strin
"b": issue.Body,
"au": issue.User.Login,
"as": strings.Join(assignees, ", "),
"rs": strings.Join(requestedReviewers, ", "),
"Mn": milestoneNumber,
"Mt": milestoneTitle,
"NC": numComments,
......@@ -369,6 +396,62 @@ func formatIssue(issue github.Issue, format string, colorize bool) string {
return ui.Expand(format, placeholders, colorize)
}
func showIssue(cmd *Command, args *Args) {
issueNumber := cmd.Arg(0)
if issueNumber == "" {
utils.Check(fmt.Errorf(cmd.Synopsis()))
}
localRepo, err := github.LocalRepo()
utils.Check(err)
project, err := localRepo.MainProject()
utils.Check(err)
gh := github.NewClient(project.Host)
var issue = &github.Issue{}
issue, err = gh.FetchIssue(project, issueNumber)
utils.Check(err)
args.NoForward()
colorize := ui.IsTerminal(os.Stdout)
if flagShowIssueFormat != "" {
ui.Printf(formatIssue(*issue, flagShowIssueFormat, colorize))
return
}
var closed = ""
if issue.State != "open" {
closed = "[CLOSED] "
}
commentsList, err := gh.FetchComments(project, issueNumber)
utils.Check(err)
ui.Printf("# %s%s\n\n", closed, issue.Title)
ui.Printf("* created by @%s on %s\n", issue.User.Login, issue.CreatedAt.String())
if len(issue.Assignees) > 0 {
var assignees []string
for _, user := range issue.Assignees {
assignees = append(assignees, user.Login)
}
ui.Printf("* assignees: %s\n", strings.Join(assignees, ", "))
}
ui.Printf("\n%s\n", issue.Body)
if issue.Comments > 0 {
ui.Printf("\n## Comments:\n")
for _, comment := range commentsList {
ui.Printf("\n### comment by @%s on %s\n\n%s\n", comment.User.Login, comment.CreatedAt.String(), comment.Body)
}
}
return
}
func createIssue(cmd *Command, args *Args) {
localRepo, err := github.LocalRepo()
utils.Check(err)
......
......@@ -72,6 +72,8 @@ pr checkout <PR-NUMBER> [<BRANCH>]
%as: comma-separated list of assignees
%rs: comma-separated list of requested reviewers
%Mn: milestone number
%Mt: milestone title
......@@ -182,6 +184,8 @@ func listPulls(cmd *Command, args *Args) {
}
if flagPullRequestSortAscending {
filters["direction"] = "asc"
} else {
filters["direction"] = "desc"
}
pulls, err := gh.FetchPullRequests(project, filters, flagPullRequestLimit, nil)
......
......@@ -72,7 +72,8 @@ pull-request -i <ISSUE>
is deprecated.
-l, --labels <LABELS>
Add a comma-separated list of labels to this pull request.
Add a comma-separated list of labels to this pull request. Labels will be
created if they don't already exist.
## Examples:
$ hub pull-request
......
......@@ -17,8 +17,8 @@ var (
cmdRelease = &Command{
Run: listReleases,
Usage: `
release [--include-drafts] [--exclude-prereleases] [-L <LIMIT>]
release show <TAG>
release [--include-drafts] [--exclude-prereleases] [-L <LIMIT>] [-f <FORMAT>]
release show [-f <FORMAT>] <TAG>
release create [-dpoc] [-a <FILE>] [-m <MESSAGE>|-F <FILE>] [-t <TARGET>] <TAG>
release edit [<options>] <TAG>
release download <TAG>
......@@ -178,6 +178,7 @@ hub(1), git-tag(1)
flagReleaseMessage,
flagReleaseFile,
flagReleaseFormat,
flagShowReleaseFormat,
flagReleaseCommitish string
flagReleaseAssets stringSliceValue
......@@ -192,6 +193,7 @@ func init() {
cmdRelease.Flag.StringVarP(&flagReleaseFormat, "format", "f", "%T%n", "FORMAT")
cmdShowRelease.Flag.BoolVarP(&flagReleaseShowDownloads, "show-downloads", "d", false, "DRAFTS")
cmdShowRelease.Flag.StringVarP(&flagShowReleaseFormat, "format", "f", "", "FORMAT")
cmdCreateRelease.Flag.BoolVarP(&flagReleaseEdit, "edit", "e", false, "EDIT")
cmdCreateRelease.Flag.BoolVarP(&flagReleaseDraft, "draft", "d", false, "DRAFT")
......@@ -315,6 +317,8 @@ func showRelease(cmd *Command, args *Args) {
gh := github.NewClient(project.Host)
args.NoForward()
if args.Noop {
ui.Printf("Would display information for `%s' release\n", tagName)
} else {
......@@ -323,6 +327,12 @@ func showRelease(cmd *Command, args *Args) {
body := strings.TrimSpace(release.Body)
colorize := ui.IsTerminal(os.Stdout)
if flagShowReleaseFormat != "" {
ui.Printf(formatRelease(*release, flagShowReleaseFormat, colorize))
return
}
ui.Println(release.Name)
if body != "" {
ui.Printf("\n%s\n", body)
......@@ -338,8 +348,6 @@ func showRelease(cmd *Command, args *Args) {
}
}
}
args.NoForward()
}
func downloadRelease(cmd *Command, args *Args) {
......
......@@ -155,3 +155,10 @@ Feature: hub compare
Aborted: the origin remote doesn't point to a GitHub repository.\n
"""
And the exit status should be 1
Scenario: Comparing two branches while not on a local branch
Given I am in detached HEAD
And I run `hub compare refactor...master`
Then the exit status should be 0
And there should be no output
And "open https://github.com/mislav/dotfiles/compare/refactor...master" should be run
......@@ -9,7 +9,7 @@ Feature: hub issue
get('/repos/github/hub/issues') {
assert :assignee => "Cornwe19",
:sort => nil,
:direction => nil
:direction => "desc"
json [
{ :number => 999,
......@@ -76,7 +76,7 @@ Feature: hub issue
get('/repos/github/hub/issues') {
assert :assignee => "Cornwe19",
:sort => nil,
:direction => nil
:direction => "desc"
json [
{ :number => 999,
......@@ -530,3 +530,125 @@ Feature: hub issue
bug
feature\n
"""
Scenario: Fetch single issue
Given the GitHub API server:
"""
get('/repos/github/hub/issues/102') {
json \
:number => 102,
:state => "open",
:body => "I want this feature",
:title => "Feature request for hub issue show",
:created_at => "2017-04-14T16:00:49Z",
:user => { :login => "royels" },
:assignees => [{:login => "royels"}],
:comments => 1
}
get('/repos/github/hub/issues/102/comments') {
json [
{ :body => "I am from the future",
:created_at => "2011-04-14T16:00:49Z",
:user => { :login => "octocat" }
},
{ :body => "I did the thing",
:created_at => "2013-10-30T22:20:00Z",
:user => { :login => "hubot" }
},
]
}
"""
When I successfully run `hub issue show 102`
Then the output should contain exactly:
"""
# Feature request for hub issue show
* created by @royels on 2017-04-14 16:00:49 +0000 UTC
* assignees: royels
I want this feature
## Comments:
### comment by @octocat on 2011-04-14 16:00:49 +0000 UTC
I am from the future
### comment by @hubot on 2013-10-30 22:20:00 +0000 UTC
I did the thing\n
"""
Scenario: Format single issue
Given the GitHub API server:
"""
get('/repos/github/hub/issues/102') {
json \
:number => 102,
:state => "open",
:body => "I want this feature",
:title => "Feature request for hub issue show",
:created_at => "2017-04-14T16:00:49Z",
:user => { :login => "royels" },
:assignees => [{:login => "royels"}],
:comments => 1
}
get('/repos/github/hub/issues/102/comments') {
json [
{ :body => "I am from the future",
:created_at => "2011-04-14T16:00:49Z",
:user => { :login => "octocat" }
},
{ :body => "I did the thing",
:created_at => "2013-10-30T22:20:00Z",
:user => { :login => "hubot" }
},
]
}
"""
When I successfully run `hub issue show 102 --format='%I %t%n%n%b%n'`
Then the output should contain exactly:
"""
102 Feature request for hub issue show
I want this feature\n
"""
Scenario: Did not supply an issue number
When I run `hub issue show`
Then the exit status should be 1
Then the output should contain exactly "Usage: hub issue show <NUMBER>\n"
Scenario: Show error message if http code is not 200 for issues endpoint
Given the GitHub API server:
"""
get('/repos/github/hub/issues/102') {
status 500
}
"""
When I run `hub issue show 102`
Then the output should contain exactly:
"""
Error fetching issue: Internal Server Error (HTTP 500)\n
"""
Scenario: Show error message if http code is not 200 for comments endpoint
Given the GitHub API server:
"""
get('/repos/github/hub/issues/102') {
json \
:number => 102,
:body => "I want this feature",
:title => "Feature request for hub issue show",
:created_at => "2017-04-14T16:00:49Z",
:user => { :login => "royels" }
}
get('/repos/github/hub/issues/102/comments') {
status 404
}
"""
When I run `hub issue show 102`
Then the output should contain exactly:
"""
Error fetching comments for issue: Not Found (HTTP 404)\n
"""
......@@ -10,7 +10,7 @@ Feature: hub pr list
assert :per_page => "100",
:page => :no,
:sort => nil,
:direction => nil
:direction => "desc"
response.headers["Link"] = %(<https://api.github.com/repositories/12345?per_page=100&page=2>; rel="next")
......@@ -63,6 +63,55 @@ Feature: hub pr list
#7 Fourth\n
"""
Scenario: List pull requests with requested reviewers
Given the GitHub API server:
"""
get('/repos/github/hub/pulls') {
assert :per_page => "100",
:page => :no,
:sort => nil,
:direction => "desc"
json [
{ :number => 999,
:title => "First",
:state => "open",
:base => {
:ref => "master",
:label => "github:master",
:repo => { :owner => { :login => "github" } }
},
:head => { :ref => "patch-1", :label => "octocat:patch-1" },
:user => { :login => "octocat" },
:requested_reviewers => [
{ :login => "rey" },
],
:requested_teams => [
{ :slug => "troopers" },
{ :slug => "cantina-band" },
]
},
{ :number => 102,
:title => "Second",
:state => "open",
:base => { :ref => "master", :label => "github:master" },
:head => { :ref => "patch-2", :label => "octocat:patch-2" },
:user => { :login => "octocat" },
:requested_reviewers => [
{ :login => "luke" },
{ :login => "jyn" },
]
},
]
}
"""
When I successfully run `hub pr list -f "%sC%>(8)%i %rs%n"`
Then the output should contain exactly:
"""
#999 rey, github/troopers, github/cantina-band
#102 luke, jyn\n
"""
Scenario: Sort by number of comments ascending
Given the GitHub API server:
"""
......
......@@ -269,18 +269,22 @@ Feature: hub pull-request
Hello
# Requesting a pull to mislav:master from mislav:topic
#
# Write a message for this pull request. The first block
# of text is the title and the rest is the description.
#
# Changes:
#
# SHA1SHA (Hub, 0 seconds ago)
# Two on topic
#
# SHA1SHA (Hub, 0 seconds ago)
# One on topic
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
Requesting a pull to mislav:master from mislav:topic
Write a message for this pull request. The first block
of text is the title and the rest is the description.
Changes:
SHA1SHA (Hub, 0 seconds ago)
Two on topic
SHA1SHA (Hub, 0 seconds ago)
One on topic
"""
......@@ -363,7 +367,7 @@ Feature: hub pull-request
"""
# Dat title
/ This line is commented out.
/ This line is not commented out.
Dem body.
"""
......@@ -371,7 +375,7 @@ Feature: hub pull-request
"""
post('/repos/mislav/coral/pulls') {
assert :title => '# Dat title',
:body => 'Dem body.'
:body => "/ This line is not commented out.\n\nDem body."
status 201
json :html_url => "the://url"
}
......@@ -873,7 +877,7 @@ Feature: hub pull-request
status 201
json :html_url => "the://url", :number => 1234,
:requested_reviewers => [{ :login => "josh" }],
:requested_teams => [{ :name => "robots" }]
:requested_teams => [{ :slug => "robots" }]
}
post('/repos/mislav/coral/pulls/1234/requested_reviewers') {
assert :reviewers => ["mislav", "pcorpet"]
......@@ -894,7 +898,7 @@ Feature: hub pull-request
status 201
json :html_url => "the://url", :number => 1234,
:requested_reviewers => [{ :login => "josh" }, { :login => "mislav" }],
:requested_teams => [{ :name => "robots" }]
:requested_teams => [{ :slug => "robots" }]
}
"""
When I successfully run `hub pull-request -m hereyougo -r mislav,josh -rgithub/robots`
......
......@@ -325,6 +325,43 @@ MARKDOWN
https://github.com/mislav/will_paginate/archive/v1.2.0.tar.gz\n
"""
Scenario: Format specific release
Given the GitHub API server:
"""
get('/repos/mislav/will_paginate/releases') {
json [
{ tag_name: 'v1.2.0',
name: 'will_paginate 1.2.0',
draft: true,
prerelease: false,
tarball_url: "https://github.com/mislav/will_paginate/archive/v1.2.0.tar.gz",
zipball_url: "https://github.com/mislav/will_paginate/archive/v1.2.0.zip",
assets: [
{ browser_download_url: "https://github.com/mislav/will_paginate/releases/download/v1.2.0/example.zip",
},
],
body: <<MARKDOWN
### Hello to my release
Here is what's broken:
- everything
MARKDOWN
},
]
}
"""
When I successfully run `hub release show v1.2.0 --format='%t (%T)%n%as%n%n%b%n'`
Then the output should contain exactly:
"""
will_paginate 1.2.0 (v1.2.0)
https://github.com/mislav/will_paginate/releases/download/v1.2.0/example.zip
### Hello to my release
Here is what's broken:
- everything\n\n
"""
Scenario: Show release no tag
When I run `hub release show`
Then the exit status should be 1
......
......@@ -28,6 +28,21 @@ func SetupTomlTestConfig() *TestConfigs {
return &TestConfigs{file.Name()}
}
func SetupTomlTestConfigWithUnixSocket() *TestConfigs {
file, _ := ioutil.TempFile("", "test-gh-config-")
content := `[[hosts]]
host = "github.com"
user = "jingweno"
access_token = "123"
protocol = "http"
unix_socket = "/tmp/go.sock"`
ioutil.WriteFile(file.Name(), []byte(content), os.ModePerm)
os.Setenv("HUB_CONFIG", file.Name())
return &TestConfigs{file.Name()}
}
func SetupTestConfigs() *TestConfigs {
file, _ := ioutil.TempFile("", "test-gh-config-")
......@@ -41,3 +56,18 @@ github.com:
return &TestConfigs{file.Name()}
}
func SetupTestConfigsWithUnixSocket() *TestConfigs {
file, _ := ioutil.TempFile("", "test-gh-config-")
content := `---
github.com:
- user: jingweno
oauth_token: 123
protocol: http
unix_socket: /tmp/go.sock`
ioutil.WriteFile(file.Name(), []byte(content), os.ModePerm)
os.Setenv("HUB_CONFIG", file.Name())
return &TestConfigs{file.Name()}
}
......@@ -525,6 +525,13 @@ func (client *Client) ForkRepository(project *Project, params map[string]interfa
return
}
type Comment struct {
Id int `json:"id"`
Body string `json:"body"`
User *User `json:"user"`
CreatedAt time.Time `json:"created_at"`
}
type Issue struct {
Number int `json:"number"`
State string `json:"state"`
......@@ -550,6 +557,8 @@ type Issue struct {
ApiUrl string `json:"url"`
HtmlUrl string `json:"html_url"`
ClosedBy *User `json:"closed_by"`
}
type PullRequest Issue
......@@ -578,7 +587,7 @@ func (pr *PullRequest) HasRequestedReviewer(name string) bool {
func (pr *PullRequest) HasRequestedTeam(name string) bool {
for _, team := range pr.RequestedTeams {
if strings.EqualFold(team.Name, name) {
if strings.EqualFold(team.Slug, name) {
return true
}
}
......@@ -596,6 +605,7 @@ type User struct {
type Team struct {
Name string `json:"name"`
Slug string `json:"slug"`
}
type Milestone struct {
......@@ -649,6 +659,38 @@ func (client *Client) FetchIssues(project *Project, filterParams map[string]inte
return
}
func (client *Client) FetchIssue(project *Project, number string) (issue *Issue, err error) {
api, err := client.simpleApi()
if err != nil {
return
}
res, err := api.Get(fmt.Sprintf("repos/%s/%s/issues/%s", project.Owner, project.Name, number))
if err = checkStatus(200, "fetching issue", res, err); err != nil {
return nil, err
}
issue = &Issue{}
err = res.Unmarshal(issue)
return
}
func (client *Client) FetchComments(project *Project, number string) (comments []Comment, err error) {
api, err := client.simpleApi()
if err != nil {
return
}
res, err := api.Get(fmt.Sprintf("repos/%s/%s/issues/%s/comments", project.Owner, project.Name, number))
if err = checkStatus(200, "fetching comments for issue", res, err); err != nil {
return nil, err
}
comments = []Comment{}
err = res.Unmarshal(&comments)
return
}
func (client *Client) CreateIssue(project *Project, params interface{}) (issue *Issue, err error) {
api, err := client.simpleApi()
if err != nil {
......@@ -848,7 +890,8 @@ func (client *Client) simpleApi() (c *simpleClient, err error) {
}
func (client *Client) apiClient() *simpleClient {
httpClient := newHttpClient(os.Getenv("HUB_TEST_HOST"), os.Getenv("HUB_VERBOSE") != "")
unixSocket := os.ExpandEnv(client.Host.UnixSocket)
httpClient := newHttpClient(os.Getenv("HUB_TEST_HOST"), os.Getenv("HUB_VERBOSE") != "", unixSocket)
apiRoot := client.absolute(normalizeHost(client.Host.Host))
if client.Host != nil && client.Host.Host != GitHubHost {
apiRoot.Path = "/api/v3/"
......
......@@ -21,6 +21,7 @@ type yamlHost struct {
User string `yaml:"user"`
OAuthToken string `yaml:"oauth_token"`
Protocol string `yaml:"protocol"`
UnixSocket string `yaml:"unix_socket,omitempty"`
}
type yamlConfig map[string][]yamlHost
......@@ -30,6 +31,7 @@ type Host struct {
User string `toml:"user"`
AccessToken string `toml:"access_token"`
Protocol string `toml:"protocol"`
UnixSocket string `toml:"unix_socket,omitempty"`
}
type Config struct {
......
......@@ -46,6 +46,7 @@ func (y *yamlConfigDecoder) Decode(r io.Reader, c *Config) error {
User: vv.User,
AccessToken: vv.OAuthToken,
Protocol: vv.Protocol,
UnixSocket: vv.UnixSocket,
}
c.Hosts = append(c.Hosts, host)
}
......
......@@ -30,6 +30,7 @@ func (y *yamlConfigEncoder) Encode(w io.Writer, c *Config) error {
User: h.User,
OAuthToken: h.AccessToken,
Protocol: h.Protocol,
UnixSocket: h.UnixSocket,
},
}
}
......
......@@ -30,6 +30,28 @@ func TestConfigService_TomlLoad(t *testing.T) {
assert.Equal(t, "http", host.Protocol)
}
func TestConfigService_TomlLoad_UnixSocket(t *testing.T) {
testConfigUnixSocket := fixtures.SetupTomlTestConfigWithUnixSocket()
defer testConfigUnixSocket.TearDown()
cc := &Config{}
cs := &configService{
Encoder: &tomlConfigEncoder{},
Decoder: &tomlConfigDecoder{},
}
err := cs.Load(testConfigUnixSocket.Path, cc)
assert.Equal(t, nil, err)
assert.Equal(t, 1, len(cc.Hosts))
host := cc.Hosts[0]
assert.Equal(t, "github.com", host.Host)
assert.Equal(t, "jingweno", host.User)
assert.Equal(t, "123", host.AccessToken)
assert.Equal(t, "http", host.Protocol)
assert.Equal(t, "/tmp/go.sock", host.UnixSocket)
}
func TestConfigService_YamlLoad(t *testing.T) {
testConfig := fixtures.SetupTestConfigs()
defer testConfig.TearDown()
......@@ -50,6 +72,28 @@ func TestConfigService_YamlLoad(t *testing.T) {
assert.Equal(t, "http", host.Protocol)
}
func TestConfigService_YamlLoad_Unix_Socket(t *testing.T) {
testConfigUnixSocket := fixtures.SetupTestConfigsWithUnixSocket()
defer testConfigUnixSocket.TearDown()
cc := &Config{}
cs := &configService{
Encoder: &yamlConfigEncoder{},
Decoder: &yamlConfigDecoder{},
}
err := cs.Load(testConfigUnixSocket.Path, cc)
assert.Equal(t, nil, err)
assert.Equal(t, 1, len(cc.Hosts))
host := cc.Hosts[0]
assert.Equal(t, "github.com", host.Host)
assert.Equal(t, "jingweno", host.User)
assert.Equal(t, "123", host.AccessToken)
assert.Equal(t, "http", host.Protocol)
assert.Equal(t, "/tmp/go.sock", host.UnixSocket)
}
func TestConfigService_TomlSave(t *testing.T) {
file, _ := ioutil.TempFile("", "test-gh-config-")
defer os.RemoveAll(file.Name())
......@@ -78,6 +122,36 @@ func TestConfigService_TomlSave(t *testing.T) {
assert.Equal(t, content, strings.TrimSpace(string(b)))
}
func TestConfigService_TomlSave_UnixSocket(t *testing.T) {
file, _ := ioutil.TempFile("", "test-gh-config-")
defer os.RemoveAll(file.Name())
host := &Host{
Host: "github.com",
User: "jingweno",
AccessToken: "123",
Protocol: "https",
UnixSocket: "/tmp/go.sock",
}
c := &Config{Hosts: []*Host{host}}
cs := &configService{
Encoder: &tomlConfigEncoder{},
Decoder: &tomlConfigDecoder{},
}
err := cs.Save(file.Name(), c)
assert.Equal(t, nil, err)
b, _ := ioutil.ReadFile(file.Name())
content := `[[hosts]]
host = "github.com"
user = "jingweno"
access_token = "123"
protocol = "https"
unix_socket = "/tmp/go.sock"`
assert.Equal(t, content, strings.TrimSpace(string(b)))
}
func TestConfigService_YamlSave(t *testing.T) {
file, _ := ioutil.TempFile("", "test-gh-config-")
defer os.RemoveAll(file.Name())
......@@ -104,3 +178,32 @@ func TestConfigService_YamlSave(t *testing.T) {
protocol: https`
assert.Equal(t, content, strings.TrimSpace(string(b)))
}
func TestConfigService_YamlSave_UnixSocket(t *testing.T) {
file, _ := ioutil.TempFile("", "test-gh-config-")
defer os.RemoveAll(file.Name())
host := &Host{
Host: "github.com",
User: "jingweno",
AccessToken: "123",
Protocol: "https",
UnixSocket: "/tmp/go.sock",
}
c := &Config{Hosts: []*Host{host}}
cs := &configService{
Encoder: &yamlConfigEncoder{},
Decoder: &yamlConfigDecoder{},
}
err := cs.Save(file.Name(), c)
assert.Equal(t, nil, err)
b, _ := ioutil.ReadFile(file.Name())
content := `github.com:
- user: jingweno
oauth_token: "123"
protocol: https
unix_socket: /tmp/go.sock`
assert.Equal(t, content, strings.TrimSpace(string(b)))
}
......@@ -14,6 +14,8 @@ import (
"github.com/github/hub/git"
)
const Scissors = "------------------------ >8 ------------------------"
func NewEditor(filename, topic, message string) (editor *Editor, err error) {
gitDir, err := git.Dir()
if err != nil {
......@@ -44,20 +46,25 @@ func NewEditor(filename, topic, message string) (editor *Editor, err error) {
}
type Editor struct {
Program string
Topic string
File string
Message string
CS string
openEditor func(program, file string) error
Program string
Topic string
File string
Message string
CS string
addedFirstComment bool
openEditor func(program, file string) error
}
func (e *Editor) AddCommentedSection(text string) {
startRegexp := regexp.MustCompilePOSIX("^")
endRegexp := regexp.MustCompilePOSIX(" +$")
commentedText := startRegexp.ReplaceAllString(text, e.CS+" ")
commentedText = endRegexp.ReplaceAllString(commentedText, "")
e.Message = e.Message + "\n" + commentedText
if !e.addedFirstComment {
scissors := e.CS + " " + Scissors + "\n"
scissors += e.CS + " Do not modify or remove the line above.\n"
scissors += e.CS + " Everything below it will be ignored.\n"
e.Message = e.Message + "\n" + scissors
e.addedFirstComment = true
}
e.Message = e.Message + "\n" + text
}
func (e *Editor) DeleteFile() error {
......@@ -75,11 +82,13 @@ func (e *Editor) EditContent() (content string, err error) {
scanner := bufio.NewScanner(reader)
unquotedLines := []string{}
scissorsLine := e.CS + " " + Scissors
for scanner.Scan() {
line := scanner.Text()
if e.CS == "" || !strings.HasPrefix(line, e.CS) {
unquotedLines = append(unquotedLines, line)
if line == scissorsLine {
break
}
unquotedLines = append(unquotedLines, line)
}
if err = scanner.Err(); err != nil {
return
......
......@@ -135,20 +135,35 @@ func (t *verboseTransport) verbosePrintln(msg string) {
fmt.Fprintln(t.Out, msg)
}
func newHttpClient(testHost string, verbose bool) *http.Client {
func newHttpClient(testHost string, verbose bool, unixSocket string) *http.Client {
var testURL *url.URL
if testHost != "" {
testURL, _ = url.Parse(testHost)
}
tr := &verboseTransport{
Transport: &http.Transport{
var httpTransport *http.Transport
if unixSocket != "" {
dialFunc := func(network, addr string) (net.Conn, error) {
return net.Dial("unix", unixSocket)
}
httpTransport = &http.Transport{
Dial: dialFunc,
DialTLS: dialFunc,
ResponseHeaderTimeout: 30 * time.Second,
ExpectContinueTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
}
} else {
httpTransport = &http.Transport{
Proxy: proxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
},
}
}
tr := &verboseTransport{
Transport: httpTransport,
Verbose: verbose,
OverrideURL: testURL,
Out: ui.Stderr,
......
......@@ -3,19 +3,32 @@ package github
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"github.com/bmizerany/assert"
)
func setupTestServer() *testServer {
func setupTestServer(unixSocket string) *testServer {
m := http.NewServeMux()
s := httptest.NewServer(m)
u, _ := url.Parse(s.URL)
if unixSocket != "" {
os.Remove(unixSocket)
unixListener, err := net.Listen("unix", unixSocket)
if err != nil {
log.Fatal("Unable to listen on unix-socket: ", err)
}
go http.Serve(unixListener, m)
}
return &testServer{
Server: s,
ServeMux: m,
......@@ -34,7 +47,7 @@ func (s *testServer) Close() {
}
func TestNewHttpClient_OverrideURL(t *testing.T) {
s := setupTestServer()
s := setupTestServer("")
defer s.Close()
s.HandleFunc("/override", func(w http.ResponseWriter, r *http.Request) {
......@@ -42,7 +55,7 @@ func TestNewHttpClient_OverrideURL(t *testing.T) {
assert.Equal(t, "example.com", r.Host)
})
c := newHttpClient(s.URL.String(), false)
c := newHttpClient(s.URL.String(), false, "")
c.Get("https://example.com/override")
s.HandleFunc("/not-override", func(w http.ResponseWriter, r *http.Request) {
......@@ -50,10 +63,25 @@ func TestNewHttpClient_OverrideURL(t *testing.T) {
assert.Equal(t, s.URL.Host, r.Host)
})
c = newHttpClient("", false)
c = newHttpClient("", false, "")
c.Get(fmt.Sprintf("%s/not-override", s.URL.String()))
}
func TestNewHttpClient_UnixSocket(t *testing.T) {
sock := "/tmp/hub-go.sock"
s := setupTestServer(sock)
defer s.Close()
s.HandleFunc("/unix-socket", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("unix-socket-works"))
})
c := newHttpClient("", false, sock)
resp, err := c.Get(fmt.Sprintf("%s/unix-socket", s.URL.String()))
assert.Equal(t, nil, err)
result, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, "unix-socket-works", string(result))
}
func TestVerboseTransport_VerbosePrintln(t *testing.T) {
var b bytes.Buffer
tr := &verboseTransport{
......
......@@ -14,6 +14,8 @@ import (
"github.com/github/hub/ui"
)
var timeNow = time.Now
func Check(err error) {
if err != nil {
ui.Errorln(err)
......@@ -113,7 +115,7 @@ func (c *Color) Brightness() float32 {
}
func TimeAgo(t time.Time) string {
duration := time.Since(t)
duration := timeNow().Sub(t)
minutes := duration.Minutes()
hours := duration.Hours()
days := hours / 24
......
......@@ -3,6 +3,7 @@ package utils
import (
"github.com/bmizerany/assert"
"testing"
"time"
)
func TestSearchBrowserLauncher(t *testing.T) {
......@@ -16,3 +17,62 @@ func TestSearchBrowserLauncher(t *testing.T) {
func TestConcatPaths(t *testing.T) {
assert.Equal(t, "foo/bar/baz", ConcatPaths("foo", "bar", "baz"))
}
func TestTimeAgo(t *testing.T) {
timeNow = func() time.Time {
return time.Date(2018, 10, 28, 14, 34, 58, 651387237, time.UTC)
}
now := timeNow()
secAgo := now.Add(-1 * time.Second)
actual := TimeAgo(secAgo)
assert.Equal(t, "now", actual)
minAgo := now.Add(-1 * time.Minute)
actual = TimeAgo(minAgo)
assert.Equal(t, "1 minute ago", actual)
minsAgo := now.Add(-5 * time.Minute)
actual = TimeAgo(minsAgo)
assert.Equal(t, "5 minutes ago", actual)
hourAgo := now.Add(-1 * time.Hour)
actual = TimeAgo(hourAgo)
assert.Equal(t, "1 hour ago", actual)
hoursAgo := now.Add(-3 * time.Hour)
actual = TimeAgo(hoursAgo)
assert.Equal(t, "3 hours ago", actual)
dayAgo := now.Add(-1 * 24 * time.Hour)
actual = TimeAgo(dayAgo)
assert.Equal(t, "1 day ago", actual)
daysAgo := now.Add(-5 * 24 * time.Hour)
actual = TimeAgo(daysAgo)
assert.Equal(t, "5 days ago", actual)
monthAgo := now.Add(-1 * 24 * 31 * time.Hour)
actual = TimeAgo(monthAgo)
assert.Equal(t, "1 month ago", actual)
monthsAgo := now.Add(-2 * 24 * 31 * time.Hour)
actual = TimeAgo(monthsAgo)
assert.Equal(t, "2 months ago", actual)
yearAgo := now.Add(-1 * 24 * 31 * 12 * time.Hour)
actual = TimeAgo(yearAgo)
assert.Equal(t, "1 year ago", actual)
yearsAgo := now.Add(-2 * 24 * 31 * 12 * time.Hour)
actual = TimeAgo(yearsAgo)
assert.Equal(t, "2 years ago", actual)
}
func TestColorBrightness(t *testing.T) {
c, err := NewColor("880000")
assert.Equal(t, nil, err)
actual := c.Brightness()
assert.Equal(t, float32(0.15946665406227112), actual)
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册