diff --git a/cfg/config.go b/cfg/config.go index 5c9118f..9b7629f 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -27,18 +27,18 @@ const dateFormat = "2006-01-02T15:04:05-0700" const defaultLogLevel = logrus.InfoLevel // fieldKey is an enum-like type to represent the customfield ID keys -type fieldKey int +type FieldKey int const ( - GitHubID fieldKey = iota - GitHubNumber fieldKey = iota - GitHubLabels fieldKey = iota - GitHubStatus fieldKey = iota - GitHubReporter fieldKey = iota - LastISUpdate fieldKey = iota + GitHubID FieldKey = iota + GitHubNumber FieldKey = iota + GitHubLabels FieldKey = iota + GitHubStatus FieldKey = iota + GitHubReporter FieldKey = iota + LastISUpdate FieldKey = iota ) -// fields represents the custom field IDs of the JIRA custom fields we care about +// Fields represents the custom field IDs of the JIRA custom fields we care about type fields struct { githubID string githubNumber string @@ -48,8 +48,26 @@ type fields struct { lastUpdate string } +type Config interface { + LoadJIRAConfig(jira.Client) (Config, error) + GetConfigFile() string + GetConfigString(string) string + IsBasicAuth() bool + GetSinceParam() time.Time + GetLogger() logrus.Entry + IsDryRun() bool + GetTimeout() time.Duration + GetFieldID(FieldKey) string + GetFieldKey(FieldKey) string + GetProject() jira.Project + GetProjectKey() string + GetRepo() (string, string) + SetJIRAToken(*oauth1.Token) + SaveConfig() error +} + // Config is the root configuration object the application creates. -type Config struct { +type realConfig struct { // cmdFile is the file Viper is using for its configuration (default $HOME/.issue-sync.json). cmdFile string // cmdConfig is the Viper configuration object created from the command line and config file. @@ -76,23 +94,29 @@ type Config struct { // holds the Viper configuration and the logger, and is validated. The // JIRA configuration is not yet initialized. func NewConfig(cmd *cobra.Command) (Config, error) { - config := Config{} - var err error - config.cmdFile, err = cmd.Flags().GetString("config") + cmdFile, err := cmd.Flags().GetString("config") if err != nil { - config.cmdFile = "" + cmdFile = "" } - config.cmdConfig = *newViper("issue-sync", config.cmdFile) - config.cmdConfig.BindPFlags(cmd.Flags()) + cmdConfig := *newViper("issue-sync", cmdFile) + cmdConfig.BindPFlags(cmd.Flags()) - config.cmdFile = config.cmdConfig.ConfigFileUsed() + cmdFile = cmdConfig.ConfigFileUsed() - config.log = *newLogger("issue-sync", config.cmdConfig.GetString("log-level")) + config := realConfig{ + cmdFile: cmdFile, + cmdConfig: cmdConfig, + log: *newLogger("issue-sync", cmdConfig.GetString("log-level")), + basicAuth: false, + fieldIDs: fields{}, + project: jira.Project{}, + since: time.Now(), + } if err := config.validateConfig(); err != nil { - return Config{}, err + return realConfig{}, err } return config, nil @@ -100,7 +124,7 @@ func NewConfig(cmd *cobra.Command) (Config, error) { // LoadJIRAConfig loads the JIRA configuration (project key, // custom field IDs) from a remote JIRA server. -func (c *Config) LoadJIRAConfig(client jira.Client) error { +func (c realConfig) LoadJIRAConfig(client jira.Client) (Config, error) { proj, res, err := client.Project.Get(c.cmdConfig.GetString("jira-project")) if err != nil { c.log.Errorf("Error retrieving JIRA project; check key and credentials. Error: %v", err) @@ -108,60 +132,61 @@ func (c *Config) LoadJIRAConfig(client jira.Client) error { body, err := ioutil.ReadAll(res.Body) if err != nil { c.log.Errorf("Error occured trying to read error body: %v", err) - return err + return c, err } c.log.Debugf("Error body: %s", body) - return errors.New(string(body)) + return c, errors.New(string(body)) } - c.project = *proj - - c.fieldIDs, err = c.getFieldIDs(client) + fields, err := c.getFieldIDs(client) if err != nil { - return err + return c, err } + c.fieldIDs = fields - return nil + c.project = *proj + + return c, nil } // GetConfigFile returns the file that Viper loaded the configuration from. -func (c Config) GetConfigFile() string { +func (c realConfig) GetConfigFile() string { return c.cmdFile } // GetConfigString returns a string value from the Viper configuration. -func (c Config) GetConfigString(key string) string { +func (c realConfig) GetConfigString(key string) string { return c.cmdConfig.GetString(key) } // IsBasicAuth is true if we're using HTTP Basic Authentication, and false if // we're using OAuth. -func (c Config) IsBasicAuth() bool { +func (c realConfig) IsBasicAuth() bool { return c.basicAuth } // GetSinceParam returns the `since` configuration parameter, parsed as a time.Time. -func (c Config) GetSinceParam() time.Time { +func (c realConfig) GetSinceParam() time.Time { return c.since } // GetLogger returns the configured application logger. -func (c Config) GetLogger() logrus.Entry { +func (c realConfig) GetLogger() logrus.Entry { return c.log } // IsDryRun returns whether the application is running in dry-run mode or not. -func (c Config) IsDryRun() bool { +func (c realConfig) IsDryRun() bool { return c.cmdConfig.GetBool("dry-run") } // GetTimeout returns the configured timeout on all API calls, parsed as a time.Duration. -func (c Config) GetTimeout() time.Duration { +func (c realConfig) GetTimeout() time.Duration { return c.cmdConfig.GetDuration("timeout") } // GetFieldID returns the customfield ID of a JIRA custom field. -func (c Config) GetFieldID(key fieldKey) string { +func (c realConfig) GetFieldID(key FieldKey) string { switch key { case GitHubID: return c.fieldIDs.githubID @@ -181,22 +206,22 @@ func (c Config) GetFieldID(key fieldKey) string { } // GetFieldKey returns customfield_XXXXX, where XXXXX is the custom field ID (see GetFieldID). -func (c Config) GetFieldKey(key fieldKey) string { +func (c realConfig) GetFieldKey(key FieldKey) string { return fmt.Sprintf("customfield_%s", c.GetFieldID(key)) } // GetProject returns the JIRA project the user has configured. -func (c Config) GetProject() jira.Project { +func (c realConfig) GetProject() jira.Project { return c.project } // GetProjectKey returns the JIRA key of the configured project. -func (c Config) GetProjectKey() string { +func (c realConfig) GetProjectKey() string { return c.project.Key } // GetRepo returns the user/org name and the repo name of the configured GitHub repository. -func (c Config) GetRepo() (string, string) { +func (c realConfig) GetRepo() (string, string) { fullName := c.cmdConfig.GetString("repo-name") parts := strings.Split(fullName, "/") // We check that repo-name is two parts separated by a slash in NewConfig, so this is safe @@ -205,7 +230,7 @@ func (c Config) GetRepo() (string, string) { // SetJIRAToken adds the JIRA OAuth tokens in the Viper configuration, ensuring that they // are saved for future runs. -func (c Config) SetJIRAToken(token *oauth1.Token) { +func (c realConfig) SetJIRAToken(token *oauth1.Token) { c.cmdConfig.Set("jira-token", token.Token) c.cmdConfig.Set("jira-secret", token.TokenSecret) } @@ -227,7 +252,7 @@ type configFile struct { } // SaveConfig updates the `since` parameter to now, then saves the configuration file. -func (c Config) SaveConfig() error { +func (c realConfig) SaveConfig() error { c.cmdConfig.Set("since", time.Now().Format(dateFormat)) var cf configFile @@ -322,7 +347,7 @@ func newLogger(app, level string) *logrus.Entry { // real URI, etc. This is the first level of checking. It does not confirm // if a JIRA cli is running at `jira-uri` for example; that is checked // in getJIRAClient when we actually make a call to the API. -func (c *Config) validateConfig() error { +func (c *realConfig) validateConfig() error { // Log level and config file location are validated already c.log.Debug("Checking config variables...") @@ -354,16 +379,6 @@ func (c *Config) validateConfig() error { } else { c.log.Debug("Using OAuth 1.0a authentication") - token := c.cmdConfig.GetString("jira-token") - if token == "" { - return errors.New("JIRA access token required") - } - - secret := c.cmdConfig.GetString("jira-secret") - if secret == "" { - return errors.New("JIRA access token secret required") - } - consumerKey := c.cmdConfig.GetString("jira-consumer-key") if consumerKey == "" { return errors.New("JIRA consumer key required for OAuth handshake") @@ -439,7 +454,7 @@ type jiraField struct { // getFieldIDs requests the metadata of every issue field in the JIRA // project, and saves the IDs of the custom fields used by issue-sync. -func (c Config) getFieldIDs(client jira.Client) (fields, error) { +func (c realConfig) getFieldIDs(client jira.Client) (fields, error) { c.log.Debug("Collecting field IDs.") req, err := client.NewRequest("GET", "/rest/api/2/field", nil) if err != nil { @@ -489,3 +504,15 @@ func (c Config) getFieldIDs(client jira.Client) (fields, error) { return fieldIDs, nil } + +func CreateTestConfig(v viper.Viper) Config { + return realConfig{ + cmdFile: "", + cmdConfig: v, + log: *logrus.New().WithField("app-name", "issue-sync-test"), + basicAuth: false, + fieldIDs: fields{}, + project: jira.Project{}, + since: time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC), + } +} diff --git a/cfg/config_test.go b/cfg/config_test.go new file mode 100644 index 0000000..78f54e9 --- /dev/null +++ b/cfg/config_test.go @@ -0,0 +1,119 @@ +package cfg + +import ( + "testing" + "time" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func TestCreateTestConfig(t *testing.T) { + v := viper.New() + v.Set("abc", 123) + v.Set("foo", "bar") + + config := CreateTestConfig(*v) + + if config.GetConfigString("foo") != "bar" { + t.Errorf("Config value `foo` wrong: expected \"bar\"; got %q", config.GetConfigString("foo")) + } + if config.GetConfigString("abc") != "123" { + t.Errorf("Config value `abc` wrong: expected \"123\"; got %q", config.GetConfigString("abc")) + } +} + +func CreateSampleCommand() cobra.Command { + cm := cobra.Command{} + + cm.Flags().String("github-token", "foobar", "") + cm.Flags().String("jira-user", "user", "") + cm.Flags().String("jira-pass", "password", "") + cm.Flags().String("repo-name", "coreos/issue-sync", "") + cm.Flags().String("jira-uri", "https://example.com/", "") + cm.Flags().String("jira-project", "TEST", "") + cm.Flags().String("since", "2017-01-01T00:00:00+0000", "") + cm.Flags().Duration("timeout", 30*time.Second, "") + + return cm +} + +func TestNewConfig(t *testing.T) { + cm := CreateSampleCommand() + + config, err := NewConfig(&cm) + if err != nil { + t.Fatalf("Error returned from config create: %v", err) + } + + if config.GetConfigString("github-token") != "foobar" { + t.Errorf("Wrong GitHub token: expected \"foobar\"; got %q", config.GetConfigString("github-token")) + } + if config.GetConfigString("jira-user") != "user" { + t.Errorf("Wrong JIRA user: expected \"user\"; got %q", config.GetConfigString("jira-user")) + } + if config.GetConfigString("jira-pass") != "password" { + t.Errorf("Wrong JIRA password: expected \"pass\"; got %q", config.GetConfigString("jira-pass")) + } + if config.GetConfigString("repo-name") != "coreos/issue-sync" { + t.Errorf("Wrong GitHub repo: expected \"coreos/issue-sync\"; got %q", config.GetConfigString("repo-name")) + } + if config.GetConfigString("jira-uri") != "https://example.com/" { + t.Errorf("Wrong JIRA URI: expected \"https://example.com/\"; got %q", config.GetConfigString("jira-uri")) + } + if config.GetConfigString("jira-project") != "TEST" { + t.Errorf("Wrong JIRA user: expected \"TEST\"; got %q", config.GetConfigString("jira-project")) + } + expectedSince := time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC) + if config.GetSinceParam().Unix() != expectedSince.Unix() { + t.Errorf("Wrong since param: expected %v; got %v", expectedSince, config.GetSinceParam()) + } + if config.GetTimeout() != 30*time.Second { + t.Errorf("Wrong timeout: expected 30s; got %v", config.GetTimeout()) + } + + if config.GetConfigFile() != "" { + t.Errorf("Config file not empty: %v", config.GetConfigFile()) + } + if config.IsDryRun() { + t.Errorf("Dry run is true; should be false!") + } + u,r := config.GetRepo() + if u != "coreos" || r != "issue-sync" { + t.Errorf("Repo is wrong! User: expected \"coreos\"; got %s. Repo: expected \"issue-sync\"; got %s", u, r) + } +} + +func TestIsBasicAuth_True(t *testing.T) { + cm := CreateSampleCommand() + + config, err := NewConfig(&cm) + if err != nil { + t.Fatalf("Error creating config: %v", err) + } + + if !config.IsBasicAuth() { + t.Errorf("Basic auth is false; should be true!") + } +} + +func TestIsBasicAuth_False(t *testing.T) { + cm := CreateSampleCommand() + cm.Flags().Set("jira-user", "") + cm.Flags().Set("jira-pass", "") + + cm.Flags().String("jira-token", "token", "") + cm.Flags().String("jira-secret", "secret", "") + // validateConfig() just checks that the file can be read, not that it's a private key + cm.Flags().String("jira-private-key", "./config_test.go", "") + cm.Flags().String("jira-consumer-key", "Private Key Name", "") + + config, err := NewConfig(&cm) + if err != nil { + t.Fatalf("Error creating config: %v", err) + } + + if config.IsBasicAuth() { + t.Errorf("Basic auth is true; should be false!") + } +} diff --git a/cmd/root.go b/cmd/root.go index 6625211..b4fe1b9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -39,7 +39,7 @@ var RootCmd = &cobra.Command{ return err } - if err := lib.CompareIssues(config, ghClient, jiraClient); err != nil { + if err := lib.CollectIssues(config, ghClient, jiraClient); err != nil { return err } diff --git a/lib/clients/github_test.go b/lib/clients/github_test.go new file mode 100644 index 0000000..db6b295 --- /dev/null +++ b/lib/clients/github_test.go @@ -0,0 +1,101 @@ +package clients + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/coreos/issue-sync/cfg" + "github.com/google/go-github/github" + "github.com/spf13/viper" +) + +func TestGitHubRequest_Pass(t *testing.T) { + v := viper.New() + v.Set("timeout", 10*time.Second) + ghClient := realGHClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *github.Response, error) { + i++ + return i, nil, nil + } + + reps, res, err := ghClient.request(f) + if err != nil { + t.Fatalf("Error was not null! Err: %v", err) + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi != 1 { + t.Errorf("Wrong request return value: expected 1; got %d", repsi) + } +} + +func TestGitHubRequest_TakesTime(t *testing.T) { + v := viper.New() + v.Set("timeout", 30*time.Second) + ghClient := realGHClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *github.Response, error) { + i++ + if i < 5 { + return i, nil, errors.New("Keep repeating!") + } + return i, nil, nil + } + + reps, res, err := ghClient.request(f) + if err != nil { + t.Fatalf("Error was not null! Err: %v", err) + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi != 5 { + t.Errorf("Wrong request return value: expected 5; got %d", repsi) + } +} + +func TestGitHubRequest_Errors(t *testing.T) { + v := viper.New() + v.Set("timeout", 5*time.Second) + ghClient := realGHClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *github.Response, error) { + i++ + return i, nil, errors.New("Keep repeating!") + } + + reps, res, err := ghClient.request(f) + if err == nil { + t.Fatalf("Error was null, but should have thrown!") + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + fmt.Printf("%v", reps) + if reps != nil { + t.Errorf("Request result was not null! Result: %v (%T)", reps, reps) + } +} diff --git a/lib/clients/jira.go b/lib/clients/jira.go index 8e7dbc0..8efdf5f 100644 --- a/lib/clients/jira.go +++ b/lib/clients/jira.go @@ -56,11 +56,11 @@ type JIRAClient interface { // on the configuration; currently, it creates either a standard // clients, or a dry-run clients. func NewJIRAClient(config *cfg.Config) (JIRAClient, error) { - log := config.GetLogger() + log := (*config).GetLogger() var oauth *http.Client var err error - if !config.IsBasicAuth() { + if !(*config).IsBasicAuth() { oauth, err = newJIRAHTTPClient(*config) if err != nil { log.Errorf("Error getting OAuth config: %v", err) @@ -70,21 +70,24 @@ func NewJIRAClient(config *cfg.Config) (JIRAClient, error) { var j JIRAClient - client, err := jira.NewClient(oauth, config.GetConfigString("jira-uri")) + client, err := jira.NewClient(oauth, (*config).GetConfigString("jira-uri")) if err != nil { log.Errorf("Error initializing JIRA clients; check your base URI. Error: %v", err) return dryrunJIRAClient{}, err } - if config.IsBasicAuth() { - client.Authentication.SetBasicAuth(config.GetConfigString("jira-user"), config.GetConfigString("jira-pass")) + if (*config).IsBasicAuth() { + client.Authentication.SetBasicAuth((*config).GetConfigString("jira-user"), (*config).GetConfigString("jira-pass")) } log.Debug("JIRA clients initialized") - config.LoadJIRAConfig(*client) + *config, err = (*config).LoadJIRAConfig(*client) + if err != nil { + return dryrunJIRAClient{}, err + } - if config.IsDryRun() { + if (*config).IsDryRun() { j = dryrunJIRAClient{ config: *config, client: *client, @@ -299,7 +302,7 @@ func (j realJIRAClient) UpdateComment(issue jira.Issue, id string, comment githu comment.GetBody(), ) - if len(body) < maxBodyLength { + if len(body) > maxBodyLength { body = body[:maxBodyLength] } @@ -318,8 +321,9 @@ func (j realJIRAClient) UpdateComment(issue jira.Issue, id string, comment githu } com, res, err := j.request(func() (interface{}, *jira.Response, error) { - res, err := j.client.Do(req, nil) - return nil, res, err + var co *jira.Comment + res, err := j.client.Do(req, co) + return co, res, err }) if err != nil { log.Errorf("Error updating comment: %v", err) diff --git a/lib/clients/jira_test.go b/lib/clients/jira_test.go new file mode 100644 index 0000000..5870184 --- /dev/null +++ b/lib/clients/jira_test.go @@ -0,0 +1,197 @@ +package clients + +import ( + "errors" + "testing" + "time" + + "github.com/andygrunwald/go-jira" + "github.com/coreos/issue-sync/cfg" + "github.com/spf13/viper" +) + +func TestRealJIRARequest_Pass(t *testing.T) { + v := viper.New() + v.Set("timeout", 10*time.Second) + jiraClient := realJIRAClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *jira.Response, error) { + i++ + return i, nil, nil + } + + reps, res, err := jiraClient.request(f) + if err != nil { + t.Fatalf("Error was not null! Err: %v", err) + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi != 1 { + t.Errorf("Wrong request return value: expected 1; got %d", repsi) + } +} + +func TestRealJIRARequest_TakesTime(t *testing.T) { + v := viper.New() + v.Set("timeout", 30*time.Second) + jiraClient := realJIRAClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *jira.Response, error) { + i++ + if i < 5 { + return i, nil, errors.New("Keep repeating!") + } + return i, nil, nil + } + + reps, res, err := jiraClient.request(f) + if err != nil { + t.Fatalf("Error was not null! Err: %v", err) + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi != 5 { + t.Errorf("Wrong request return value: expected 5; got %d", repsi) + } +} + +func TestRealRequest_Errors(t *testing.T) { + v := viper.New() + v.Set("timeout", 5*time.Second) + jiraClient := realJIRAClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *jira.Response, error) { + i++ + return i, nil, errors.New("Keep repeating!") + } + + reps, res, err := jiraClient.request(f) + if err == nil { + t.Fatalf("Error was null, but should have thrown!") + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi < 4 { + t.Errorf("Wrong request return value: expected at least 4; got %d", repsi) + } +} + +func TestDryrunJIRARequest_Pass(t *testing.T) { + v := viper.New() + v.Set("timeout", 10*time.Second) + jiraClient := realJIRAClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *jira.Response, error) { + i++ + return i, nil, nil + } + + reps, res, err := jiraClient.request(f) + if err != nil { + t.Fatalf("Error was not null! Err: %v", err) + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi != 1 { + t.Errorf("Wrong request return value: expected 1; got %d", repsi) + } +} + +func TestDryrunJIRARequest_TakesTime(t *testing.T) { + v := viper.New() + v.Set("timeout", 30*time.Second) + jiraClient := realJIRAClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *jira.Response, error) { + i++ + if i < 5 { + return i, nil, errors.New("Keep repeating!") + } + return i, nil, nil + } + + reps, res, err := jiraClient.request(f) + if err != nil { + t.Fatalf("Error was not null! Err: %v", err) + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi != 5 { + t.Errorf("Wrong request return value: expected 5; got %d", repsi) + } +} + +func TestDryrunRequest_Errors(t *testing.T) { + v := viper.New() + v.Set("timeout", 5*time.Second) + jiraClient := realJIRAClient{ + config: cfg.CreateTestConfig(*v), + } + + i := 0 + f := func() (interface{}, *jira.Response, error) { + i++ + return i, nil, errors.New("Keep repeating!") + } + + reps, res, err := jiraClient.request(f) + if err == nil { + t.Fatalf("Error was null, but should have thrown!") + } + if res != nil { + t.Fatalf("Response was not null! Res: %v (%T)", res, res) + } + + repsi, ok := reps.(int) + if !ok { + t.Fatalf("Wrong request return type: expected int; got %T", reps) + } + if repsi < 4 { + t.Errorf("Wrong request return value: expected at least 4; got %d", repsi) + } +} diff --git a/lib/clients/oauth_test.go b/lib/clients/oauth_test.go new file mode 100644 index 0000000..2ea3902 --- /dev/null +++ b/lib/clients/oauth_test.go @@ -0,0 +1 @@ +package clients diff --git a/lib/comments_test.go b/lib/comments_test.go new file mode 100644 index 0000000..d808093 --- /dev/null +++ b/lib/comments_test.go @@ -0,0 +1,71 @@ +package lib + +import "testing" + +func TestCompareComments(t *testing.T) { + setup() + + if err := setup(); err != nil { + t.Fatalf("Error running setup: %v", err) + } + + gh := testGHClient{} + jc := &testJIRAClient{} + + config := NewTestConfig() + + ghIssue := ghIssues[0] + jIssue := jiraIssues[0] + + err := CompareComments(config, ghIssue, jIssue, gh, jc) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(jc.UpdatedComments) != 1 { + t.Fatalf("Wrong number of updated comments: expected 1; got %d", len(jc.UpdatedComments)) + } + com := jc.UpdatedComments[0] + if com.Issue.ID != jIssue.ID { + t.Errorf("Wrong updated comment issue ID: expected %s; got %s", jIssue.ID, com.Issue.ID) + } + if com.GHComment.GetID() != ghComments[ghIssue.GetID()][1].GetID() { + t.Errorf("Wrong updated comment GH ID: expected %d; got %d", ghComments[ghIssue.GetID()][1].GetID(), com.GHComment.GetID()) + } + if com.GHComment.GetBody() != ghComments[ghIssue.GetID()][1].GetBody() { + t.Errorf("Wrong updated comment GH body: expected %d; got %d", ghComments[ghIssue.GetID()][1].GetBody(), com.GHComment.GetBody()) + } + + if len(jc.CreatedComments) != 1 { + t.Fatalf("Wrong number of created comments: expected 1; got %d", len(jc.CreatedComments)) + } +} + +func TestCompareComments_Same(t *testing.T) { + setup() + + if err := setup(); err != nil { + t.Fatalf("Error running setup: %v", err) + } + + gh := testGHClient{} + jc := &testJIRAClient{} + + config := NewTestConfig() + + ghIssue := ghIssues[1] + jIssue := jiraIssues[1] + + err := CompareComments(config, ghIssue, jIssue, gh, jc) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(jc.UpdatedComments) != 0 { + t.Fatalf("Wrong number of updated comments: expected 0; got %d", len(jc.UpdatedComments)) + } + + if len(jc.CreatedComments) != 0 { + t.Fatalf("Wrong number of created comments: expected 0; got %d", len(jc.CreatedComments)) + } +} diff --git a/lib/issues.go b/lib/issues.go index 20bdf38..108aff4 100644 --- a/lib/issues.go +++ b/lib/issues.go @@ -13,23 +13,62 @@ import ( // dateFormat is the format used for the Last IS Update field const dateFormat = "2006-01-02T15:04:05-0700" +type issuePair struct { + gh github.Issue + j jira.Issue +} + +func CollectIssues(config cfg.Config, ghClient clients.GitHubClient, jiraClient clients.JIRAClient) error { + log := config.GetLogger() + + update, create, err := CompareIssues(config, ghClient, jiraClient) + if err != nil { + return err + } + + for _, v := range update { + if DidIssueChange(config, v) { + if err = UpdateIssue(config, v, jiraClient); err != nil { + log.Errorf("Error updating issue: %v", err) + continue + } + } + if err = CompareComments(config, v.gh, v.j, ghClient, jiraClient); err != nil { + log.Errorf("Error comparing comments: %v", err) + } + } + + for _, v := range create { + jIssue, err := CreateIssue(config, v, jiraClient) + if err != nil { + log.Errorf("Error creating issue: %v", err) + continue + } + if err = CompareComments(config, v, jIssue, ghClient, jiraClient); err != nil { + log.Errorf("Error comparing comments: %v", err) + } + } + + return nil +} + // CompareIssues gets the list of GitHub issues updated since the `since` date, // gets the list of JIRA issues which have GitHub ID custom fields in that list, // then matches each one. If a JIRA issue already exists for a given GitHub issue, // it calls UpdateIssue; if no JIRA issue already exists, it calls CreateIssue. -func CompareIssues(config cfg.Config, ghClient clients.GitHubClient, jiraClient clients.JIRAClient) error { +func CompareIssues(config cfg.Config, ghClient clients.GitHubClient, jiraClient clients.JIRAClient) ([]issuePair, []github.Issue, error) { log := config.GetLogger() log.Debug("Collecting issues") ghIssues, err := ghClient.ListIssues() if err != nil { - return err + return []issuePair{}, []github.Issue{}, err } if len(ghIssues) == 0 { log.Info("There are no GitHub issues; exiting") - return nil + return []issuePair{}, []github.Issue{}, nil } ids := make([]int, len(ghIssues)) @@ -39,9 +78,12 @@ func CompareIssues(config cfg.Config, ghClient clients.GitHubClient, jiraClient jiraIssues, err := jiraClient.ListIssues(ids) if err != nil { - return err + return []issuePair{}, []github.Issue{}, err } + var updatePairs []issuePair + var createIssues []github.Issue + log.Debug("Collected all JIRA issues") for _, ghIssue := range ghIssues { @@ -50,27 +92,30 @@ func CompareIssues(config cfg.Config, ghClient clients.GitHubClient, jiraClient id, _ := jIssue.Fields.Unknowns.Int(config.GetFieldKey(cfg.GitHubID)) if int64(*ghIssue.ID) == id { found = true - if err := UpdateIssue(config, ghIssue, jIssue, ghClient, jiraClient); err != nil { - log.Errorf("Error updating issue %s. Error: %v", jIssue.Key, err) + pair := issuePair{ + gh: ghIssue, + j: jIssue, } + updatePairs = append(updatePairs, pair) break } } if !found { - if err := CreateIssue(config, ghIssue, ghClient, jiraClient); err != nil { - log.Errorf("Error creating issue for #%d. Error: %v", *ghIssue.Number, err) - } + createIssues = append(createIssues, ghIssue) } } - return nil + return updatePairs, createIssues, nil } // DidIssueChange tests each of the relevant fields on the provided JIRA and GitHub issue // and returns whether or not they differ. -func DidIssueChange(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue) bool { +func DidIssueChange(config cfg.Config, issues issuePair) bool { log := config.GetLogger() + ghIssue := issues.gh + jIssue := issues.j + log.Debugf("Comparing GitHub issue #%d and JIRA issue %s", ghIssue.GetNumber(), jIssue.Key) anyDifferent := false @@ -101,7 +146,7 @@ func DidIssueChange(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue) anyDifferent = true } - log.Debugf("Issues have any differences: %b", anyDifferent) + log.Debugf("Issues have any differences: %t", anyDifferent) return anyDifferent } @@ -109,14 +154,17 @@ func DidIssueChange(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue) // UpdateIssue compares each field of a GitHub issue to a JIRA issue; if any of them // differ, the differing fields of the JIRA issue are updated to match the GitHub // issue. -func UpdateIssue(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue, ghClient clients.GitHubClient, jClient clients.JIRAClient) error { +func UpdateIssue(config cfg.Config, issues issuePair, jClient clients.JIRAClient) error { log := config.GetLogger() + ghIssue := issues.gh + jIssue := issues.j + log.Debugf("Updating JIRA %s with GitHub #%d", jIssue.Key, *ghIssue.Number) var issue jira.Issue - if DidIssueChange(config, ghIssue, jIssue) { + if DidIssueChange(config, issues) { fields := jira.IssueFields{} fields.Unknowns = map[string]interface{}{} @@ -158,16 +206,12 @@ func UpdateIssue(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue, ghC return err } - if err := CompareComments(config, ghIssue, issue, ghClient, jClient); err != nil { - return err - } - return nil } // CreateIssue generates a JIRA issue from the various fields on the given GitHub issue, then // sends it to the JIRA API. -func CreateIssue(config cfg.Config, issue github.Issue, ghClient clients.GitHubClient, jClient clients.JIRAClient) error { +func CreateIssue(config cfg.Config, issue github.Issue, jClient clients.JIRAClient) (jira.Issue, error) { log := config.GetLogger() log.Debugf("Creating JIRA issue based on GitHub issue #%d", *issue.Number) @@ -201,19 +245,15 @@ func CreateIssue(config cfg.Config, issue github.Issue, ghClient clients.GitHubC jIssue, err := jClient.CreateIssue(jIssue) if err != nil { - return err + return jira.Issue{}, err } jIssue, err = jClient.GetIssue(jIssue.Key) if err != nil { - return err + return jira.Issue{}, err } log.Debugf("Created JIRA issue %s!", jIssue.Key) - if err := CompareComments(config, issue, jIssue, ghClient, jClient); err != nil { - return err - } - - return nil + return jIssue, nil } diff --git a/lib/issues_test.go b/lib/issues_test.go new file mode 100644 index 0000000..3eedf9e --- /dev/null +++ b/lib/issues_test.go @@ -0,0 +1,384 @@ +package lib + +import ( + "encoding/json" + "io/ioutil" + "os" + "strings" + "testing" + "time" + + "github.com/andygrunwald/go-jira" + "github.com/coreos/issue-sync/lib/clients" + "github.com/google/go-github/github" +) + +/* + * There are three testing GitHub issues. The first has two comments, the second and third have 1. + * The first and second are open, and the third closed. There are three associated users: tester, + * testguy, and test3. The first issue has an identical JIRA issue, although only one comment has + * been copied. The second issue has a matching, but outdated, JIRA issue, so it will be updated. + * The third issue has no JIRA issue, so a new one will be created. + */ + +const defaultLastUpdate = "2017-08-21T00:00:00Z" + +var ghIssues []github.Issue + +var ghComments map[int][]*github.IssueComment + +var jiraIssues = []jira.Issue{ + { + ID: "10001", + Fields: &jira.IssueFields{ + Summary: "Test Issue 1", + Description: "First issue used for testing", + Comments: &jira.Comments{ + Comments: []*jira.Comment{ + { + ID: "10002", + Body: "Comment [(ID 10)|http://example.com/comment/10] from GitHub user " + + "[tester|http://example.com/tester] (Test User) at 12:00 PM, August 22 2017:\n\n" + + "First comment on the first issue!", + }, + { + ID: "10005", + Body: "Comment [(ID 12)|http://example.com/comment/12] from GitHub user " + + "[tester|http://example.com/tester] (Test user) at 15:00 PM, August 22 2017:\n\n" + + "Second comment on the first issue!", + }, + }, + }, + Unknowns: map[string]interface{}{ + ghIDFieldKey: int64(1), + ghNumberFieldKey: int64(1), + ghLabelsFieldKey: "label1", + ghStatusFieldKey: "open", + ghReporterFieldKey: "tester", + isLastUpdateFieldKey: defaultLastUpdate, + }, + }, + }, + { + ID: "10003", + Fields: &jira.IssueFields{ + Summary: "Test Issue 2", + Description: "Second issue used for testing", + Comments: &jira.Comments{ + Comments: []*jira.Comment{ + { + ID: "10004", + Body: "Comment [(ID 11)|http://example.com/comment/11] from GitHub user " + + "[testguy|http://example.com/testguy] (Another Test User) at 18:00 PM, August 22 2017:\n\n" + + "First comment on the second issue!", + }, + }, + }, + Unknowns: map[string]interface{}{ + ghIDFieldKey: int64(2), + ghNumberFieldKey: int64(2), + ghLabelsFieldKey: "label2,label3", + ghStatusFieldKey: "open", + ghReporterFieldKey: "testguy", + isLastUpdateFieldKey: defaultLastUpdate, + }, + }, + }, +} + +type testGHClient struct { +} + +func (testGHClient) ListIssues() ([]github.Issue, error) { + return ghIssues, nil +} + +func (testGHClient) ListComments(issue github.Issue) ([]*github.IssueComment, error) { + return ghComments[issue.GetID()], nil +} + +func (testGHClient) GetUser(login string) (github.User, error) { + for _, v := range ghIssues { + if v.User.GetLogin() == login { + return *v.User, nil + } + } + return github.User{}, nil +} + +func (testGHClient) GetRateLimits() (github.RateLimits, error) { + return github.RateLimits{ + Core: &github.Rate{ + Limit: 1<<32 - 1, + Remaining: 1<<32 - 1, + Reset: github.Timestamp{ + Time: time.Date(2100, 1, 1, 0, 0, 0, 0, nil), + }, + }, + }, nil +} + +type comment struct { + Issue jira.Issue + ID string + GHComment github.IssueComment +} + +type testJIRAClient struct { + CreatedIssues []jira.Issue + UpdatedIssues []jira.Issue + CreatedComments []comment + UpdatedComments []comment +} + +func (*testJIRAClient) ListIssues(ids []int) ([]jira.Issue, error) { + return jiraIssues, nil +} + +func (*testJIRAClient) GetIssue(key string) (jira.Issue, error) { + for _, v := range jiraIssues { + if v.Key == key { + return v, nil + } + } + return jira.Issue{}, nil +} + +func (j *testJIRAClient) CreateIssue(issue jira.Issue) (jira.Issue, error) { + j.CreatedIssues = append(j.CreatedIssues, issue) + return issue, nil +} + +func (j *testJIRAClient) UpdateIssue(issue jira.Issue) (jira.Issue, error) { + j.UpdatedIssues = append(j.UpdatedIssues, issue) + return issue, nil +} + +func (j *testJIRAClient) CreateComment(issue jira.Issue, cmnt github.IssueComment, github clients.GitHubClient) (jira.Comment, error) { + j.CreatedComments = append(j.CreatedComments, comment{ + Issue: issue, + ID: "", + GHComment: cmnt, + }) + + return jira.Comment{}, nil +} + +func (j *testJIRAClient) UpdateComment(issue jira.Issue, id string, cmnt github.IssueComment, github clients.GitHubClient) (jira.Comment, error) { + j.UpdatedComments = append(j.UpdatedComments, comment{ + Issue: issue, + ID: id, + GHComment: cmnt, + }) + + return jira.Comment{}, nil +} + +func setup() error { + file, err := os.Open("sample_gh_issues.json") + if err != nil { + return err + } + jsonData, err := ioutil.ReadAll(file) + if err != nil { + return err + } + err = json.Unmarshal(jsonData, &ghIssues) + if err != nil { + return err + } + + file, err = os.Open("sample_comments.json") + if err != nil { + return err + } + jsonData, err = ioutil.ReadAll(file) + if err != nil { + return err + } + err = json.Unmarshal(jsonData, &ghComments) + return err +} + +func TestDidIssueChange_Same(t *testing.T) { + setup() + + if err := setup(); err != nil { + t.Fatalf("Error running setup: %v", err) + } + + config := NewTestConfig() + + pair := issuePair{ + gh: ghIssues[0], + j: jiraIssues[0], + } + + if DidIssueChange(config, pair) { + t.Errorf("Issue did change?") + t.Errorf("%+v", pair.gh) + t.Errorf("%+v", pair.j) + } +} + +func TestDidIssueChange_Different(t *testing.T) { + setup() + + if err := setup(); err != nil { + t.Fatalf("Error running setup: %v", err) + } + + config := NewTestConfig() + + pair := issuePair{ + gh: ghIssues[1], + j: jiraIssues[1], + } + + if !DidIssueChange(config, pair) { + t.Errorf("Issue did not change?") + t.Errorf("%+v", pair.gh) + t.Errorf("%+v", pair.j) + } +} + +func TestCompareIssues(t *testing.T) { + if err := setup(); err != nil { + t.Fatalf("Error running setup: %v", err) + } + + gh := testGHClient{} + jc := &testJIRAClient{} + + config := NewTestConfig() + + updates, creates, err := CompareIssues(config, gh, jc) + if err != nil { + t.Fatalf("Error occured while comparing issues: %v", err) + } + + if len(updates) != 1 { + t.Fatalf("Number of updates wrong: expected 1; got %d", len(updates)) + } + if updates[0].gh.ID != ghIssues[1].ID { + t.Errorf("Update pair GitHub issue ID wrong: expected %d; got %d", ghIssues[1].ID, updates[0].gh.ID) + } + if updates[0].j.ID != jiraIssues[1].ID { + t.Errorf("Update pair JIRA issue ID wrong: expected %d; got %d", jiraIssues[1].ID, updates[0].j.ID) + } + + if len(creates) != 1 { + t.Fatalf("Number of creates wrong: expected 1; got %d", len(creates)) + } + if creates[0].ID != ghIssues[2].ID { + t.Errorf("Create issue ID wrong: expected %d; got %d", ghIssues[2].ID, creates[0].ID) + } +} + +func TestCreateIssue(t *testing.T) { + setup() + + if err := setup(); err != nil { + t.Fatalf("Error running setup: %v", err) + } + + gh := testGHClient{} + jc := &testJIRAClient{} + + config := NewTestConfig() + + issue := ghIssues[2] + + err := CreateIssue(config, issue, gh, jc) + if err != nil { + t.Fatalf("Error: %v", err) + } + + if len(jc.CreatedIssues) != 1 { + t.Fatalf("Wrong number of created issues: expected 1; got %d", len(jc.CreatedIssues)) + } + + i := jc.CreatedIssues[0].Fields + if i.Summary != issue.GetTitle() { + t.Errorf("Summary is wrong: expected %s; got %s", issue.GetTitle(), i.Summary) + } + if i.Description != issue.GetBody() { + t.Errorf("Description is wrong: expected %s; got %s", issue.GetBody(), i.Description) + } + if i.Unknowns[ghIDFieldKey] != issue.GetID() { + t.Errorf("GitHub ID is wrong: expected %d; got %d", issue.GetID(), i.Unknowns[ghIDFieldKey]) + } + if i.Unknowns[ghNumberFieldKey] != issue.GetNumber() { + t.Errorf("GitHub Number is wrong: expected %d; got %d", issue.GetNumber(), i.Unknowns[ghNumberFieldKey]) + } + labels := make([]string, len(issue.Labels)) + for i, v := range issue.Labels { + labels[i] = v.GetName() + } + if i.Unknowns[ghLabelsFieldKey] != strings.Join(labels, ",") { + t.Errorf("GitHub Labels are wrong: expected %s; got %s", strings.Join(labels, ","), i.Unknowns[ghLabelsFieldKey]) + } + if i.Unknowns[ghStatusFieldKey] != issue.GetState() { + t.Errorf("GitHub Status is wrong: expected %s; got %s", issue.GetState(), i.Unknowns[ghStatusFieldKey]) + } + if i.Unknowns[ghReporterFieldKey] != issue.User.GetLogin() { + t.Errorf("GitHub Reporter is wrong: expected %s; got %s", issue.User.GetLogin(), i.Unknowns[ghReporterFieldKey]) + } + if i.Unknowns[isLastUpdateFieldKey] != time.Now().Format(dateFormat) { + t.Errorf("Last Update is wrong: expected %s; got %s", time.Now().Format(dateFormat), i.Unknowns[isLastUpdateFieldKey]) + } +} + +func TestUpdateIssue(t *testing.T) { + setup() + + if err := setup(); err != nil { + t.Fatalf("Error running setup: %v", err) + } + + gh := testGHClient{} + jc := &testJIRAClient{} + + config := NewTestConfig() + + ghIssue := ghIssues[1] + jIssue := jiraIssues[1] + + pair := issuePair{ + gh: ghIssue, + j: jIssue, + } + + err := UpdateIssue(config, pair, gh, jc) + if err != nil { + t.Fatalf("Error: %v", err) + } + + if len(jc.UpdatedIssues) != 1 { + t.Fatalf("Wrong number of created issues: expected 1; got %d", len(jc.CreatedIssues)) + } + + i := jc.UpdatedIssues[0].Fields + if i.Summary != ghIssue.GetTitle() { + t.Errorf("Summary is wrong: expected %s; got %s", ghIssue.GetTitle(), i.Summary) + } + if i.Description != ghIssue.GetBody() { + t.Errorf("Description is wrong: expected %s; got %s", ghIssue.GetBody(), i.Description) + } + labels := make([]string, len(ghIssue.Labels)) + for i, v := range ghIssue.Labels { + labels[i] = v.GetName() + } + if i.Unknowns[ghLabelsFieldKey] != strings.Join(labels, ",") { + t.Errorf("GitHub Labels are wrong: expected %s; got %s", strings.Join(labels, ","), i.Unknowns[ghLabelsFieldKey]) + } + if i.Unknowns[ghStatusFieldKey] != ghIssue.GetState() { + t.Errorf("GitHub Status is wrong: expected %s; got %s", ghIssue.GetState(), i.Unknowns[ghStatusFieldKey]) + } + if i.Unknowns[ghReporterFieldKey] != ghIssue.User.GetLogin() { + t.Errorf("GitHub Reporter is wrong: expected %s; got %s", ghIssue.User.GetLogin(), i.Unknowns[ghReporterFieldKey]) + } + if i.Unknowns[isLastUpdateFieldKey] != time.Now().Format(dateFormat) { + t.Errorf("Last Update is wrong: expected %s; got %s", time.Now().Format(dateFormat), i.Unknowns[isLastUpdateFieldKey]) + } +} diff --git a/lib/sample_comments.json b/lib/sample_comments.json new file mode 100644 index 0000000..981707a --- /dev/null +++ b/lib/sample_comments.json @@ -0,0 +1,68 @@ +{ + "1": [ + { + "id": 10, + "body": "First comment on the first issue!", + "user": { + "id": 2, + "login": "testguy", + "name": "Another Test user", + "html_url": "http://example.com/testguy" + }, + "html_url": "http://example.com/comment/10", + "created_at": "2017-08-22T12:00:00Z" + }, + { + "id": 12, + "body": "Second comment on the first issue, which has been edited!", + "user": { + "id": 1, + "login": "tester", + "name": "Test user", + "html_url": "http://example.com/tester" + }, + "html_url": "http://example.com/comment/12", + "created_at": "2017-08-22T15:00:00Z" + }, + { + "id": 14, + "body": "Third comment on the first issue!", + "user": { + "id": 3, + "login": "test3", + "name": "Third Test User", + "html_url": "http://example.com/test3" + }, + "html_url": "http://example.com/comment/14", + "created_at": "2017-08-23T08:00:00Z" + } + ], + "2": [ + { + "id": 11, + "body": "First comment on the second issue!", + "user": { + "id": 2, + "login": "testguy", + "name": "Another Test user", + "html_url": "http://example.com/testguy" + }, + "html_url": "http://example.com/comment/11", + "created_at": "2017-08-22T18:00:00Z" + } + ], + "3": [ + { + "id": 13, + "body": "Comment on the third issue!", + "user": { + "id": 1, + "login": "tester", + "name": "Test user", + "html_url": "http://example.com/tester" + }, + "html_url": "http://example.com/comment/13", + "created_at": "2017-08-22T21:00:00Z" + } + ] +} \ No newline at end of file diff --git a/lib/sample_gh_issues.json b/lib/sample_gh_issues.json new file mode 100644 index 0000000..af2a3bc --- /dev/null +++ b/lib/sample_gh_issues.json @@ -0,0 +1,70 @@ +[ + { + "id": 1, + "number": 1, + "state": "open", + "title": "Test Issue 1", + "body": "First issue used for testing", + "user": { + "id": 1, + "login": "tester", + "name": "Test User", + "html_url": "http://example.com/tester" + }, + "labels": [ + { + "id": 1, + "name": "label1" + } + ], + "comments": 2 + }, + { + "id": 2, + "number": 2, + "state": "open", + "title": "Test Issue 2", + "body": "Second issue used for testing, which has been changed", + "user": { + "id": 2, + "login": "testguy", + "name": "Another Test User", + "html_url": "http://example.com/testguy" + }, + "labels": [ + { + "id": 2, + "name": "label2" + }, + { + "id": 4, + "name": "label4" + } + ], + "comments": 1 + }, + { + "id": 3, + "number": 3, + "state": "closed", + "title": "Test Issue 3", + "body": "Third issue used for testing", + "user": { + "id": 3, + "login": "test3", + "name": "Third Test User", + "html_url": "http://example.com/test3" + }, + "labels": [ + { + "id": 1, + "name": "label1" + }, + { + "id": 4, + "name": "label4" + } + ], + "comments": 1 + } +] \ No newline at end of file diff --git a/lib/setup_test.go b/lib/setup_test.go new file mode 100644 index 0000000..eb4393e --- /dev/null +++ b/lib/setup_test.go @@ -0,0 +1,123 @@ +package lib + +import ( + "fmt" + "time" + + "github.com/Sirupsen/logrus" + "github.com/andygrunwald/go-jira" + "github.com/coreos/issue-sync/cfg" + "github.com/dghubble/oauth1" +) + +/* This file holds functions and structures to be used by all of the lib/ tests, + * including a test cfg.Config object. + */ + +const ghIDFieldID = "10101" +const ghNumberFieldID = "10102" +const ghLabelsFieldID = "10103" +const ghStatusFieldID = "10104" +const ghReporterFieldID = "10105" +const isLastUpdateFieldID = "10106" + +const ghIDFieldKey = "customfield_10101" +const ghNumberFieldKey = "customfield_10102" +const ghLabelsFieldKey = "customfield_10103" +const ghStatusFieldKey = "customfield_10104" +const ghReporterFieldKey = "customfield_10105" +const isLastUpdateFieldKey = "customfield_10106" + +func NewTestConfig() cfg.Config { + var config cfg.Config + config = testConfig{ + log: *logrus.New().WithField("app-name", "issue-sync-test"), + } + + return config +} + +type testConfig struct { + log logrus.Entry +} + +func (c testConfig) LoadJIRAConfig(jira.Client) (cfg.Config, error) { + return c, nil +} + +func (testConfig) GetConfigFile() string { + return "" +} + +func (testConfig) GetConfigString(string) string { + return "" +} + +func (testConfig) IsBasicAuth() bool { + return false +} + +func (testConfig) GetSinceParam() time.Time { + return time.Now() +} + +func (c testConfig) GetLogger() logrus.Entry { + return c.log +} + +func (testConfig) IsDryRun() bool { + return false +} + +func (testConfig) GetTimeout() time.Duration { + return time.Second +} + +func (testConfig) GetFieldID(key cfg.FieldKey) string { + switch key { + case cfg.GitHubID: + return ghIDFieldID + case cfg.GitHubNumber: + return ghNumberFieldID + case cfg.GitHubLabels: + return ghLabelsFieldID + case cfg.GitHubStatus: + return ghStatusFieldID + case cfg.GitHubReporter: + return ghReporterFieldID + case cfg.LastISUpdate: + return isLastUpdateFieldID + default: + return "" + } +} + +func (c testConfig) GetFieldKey(key cfg.FieldKey) string { + return fmt.Sprintf("customfield_%s", c.GetFieldID(key)) +} + +func (testConfig) GetProject() jira.Project { + return jira.Project{ + Key: "TEST", + Name: "Test", + } +} + +func (testConfig) GetProjectKey() string { + return "TEST" +} + +func (testConfig) GetRepo() (string, string) { + return "coreos", "issue-sync" +} + +func (testConfig) SetJIRAToken(*oauth1.Token) { +} + +func (testConfig) SaveConfig() error { + return nil +} + +func (testConfig) validateConfig() error { + return nil +}