From b8c500fb9025761bb18c99530a9dea24ebd25bb9 Mon Sep 17 00:00:00 2001 From: Ryan Nixon Date: Sun, 16 Aug 2020 16:31:55 -0700 Subject: [PATCH] Expand test coverage, reinforce mocks (#16) * Expand test coverage, reinforce mocks * A little more coverage around config --- .github/workflows/acceptance.yml | 5 ++- .github/workflows/unit.yml | 2 +- example/job.tf | 10 ++++-- example/providers.tf | 2 +- jenkins/config.go | 28 ++++++++++----- jenkins/config_test.go | 45 +++++++++++++++++++++++-- jenkins/provider.go | 15 +++++++-- jenkins/resource_jenkins_folder_test.go | 3 +- jenkins/resource_jenkins_job.go | 5 ++- jenkins/resource_jenkins_job_test.go | 2 +- 10 files changed, 90 insertions(+), 27 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 34ad66d..a190c12 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -34,4 +34,7 @@ jobs: JENKINS_URL: "http://localhost:8080" JENKINS_USERNAME: "admin" JENKINS_PASSWORD: "admin" - run: go test -v -cover ./... + run: go test -v -covermode=atomic -coverprofile=coverage.txt ./... + + - name: Send coverage to Codecov + run: bash <(curl -s https://codecov.io/bash) -Z -f coverage.txt -F acceptance diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index b9c3537..b6a4d46 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -25,4 +25,4 @@ jobs: run: go test -race -covermode=atomic -coverprofile=coverage.txt ./... - name: Send coverage to Codecov - run: bash <(curl -s https://codecov.io/bash) -Z -f coverage.txt + run: bash <(curl -s https://codecov.io/bash) -Z -f coverage.txt -F unit diff --git a/example/job.tf b/example/job.tf index 3804fc0..f3558d0 100644 --- a/example/job.tf +++ b/example/job.tf @@ -1,7 +1,7 @@ resource jenkins_folder example { - name = "folder-name" - description = "A sample folder" - template = < {{ .Description }} @@ -16,6 +16,10 @@ resource jenkins_folder example { EOT + + lifecycle { + ignore_changes = [template] + } } resource jenkins_job pipeline { diff --git a/example/providers.tf b/example/providers.tf index cd75589..8764c52 100644 --- a/example/providers.tf +++ b/example/providers.tf @@ -1,5 +1,5 @@ # Connects to the instance launched through "docker-compose up -d" -# Once done with testing, clean up the instance with "docker-compose down" +# Once done with testing, clean up the instance with "docker-compose down --volumes" provider jenkins { server_url = "http://localhost:8080" # Or use JENKINS_URL env var username = "admin" # Or use JENKINS_USERNAME env var diff --git a/jenkins/config.go b/jenkins/config.go index 5ea1ae1..b027371 100644 --- a/jenkins/config.go +++ b/jenkins/config.go @@ -1,35 +1,45 @@ package jenkins import ( + "io" "io/ioutil" jenkins "github.com/bndr/gojenkins" ) type jenkinsClient interface { + CreateJobInFolder(config string, jobName string, parentIDs ...string) (*jenkins.Job, error) + Credentials() *jenkins.CredentialsManager DeleteJob(name string) (bool, error) GetJob(id string, parentIDs ...string) (*jenkins.Job, error) } +// jenkinsAdapter wraps the Jenkins client, enabling additional functionality +type jenkinsAdapter struct { + *jenkins.Jenkins +} + // Config is the set of parameters needed to configure the Jenkins provider. type Config struct { ServerURL string - CACert string + CACert io.Reader Username string Password string } -func newJenkinsClient(c *Config) (*jenkins.Jenkins, error) { +func newJenkinsClient(c *Config) *jenkinsAdapter { client := jenkins.CreateJenkins(nil, c.ServerURL, c.Username, c.Password) - if c.CACert != "" { + if c.CACert != nil { // provide CA certificate if server is using self-signed certificate - client.Requester.CACert, _ = ioutil.ReadFile(c.CACert) - } - _, err := client.Init() - if err != nil { - return nil, err + client.Requester.CACert, _ = ioutil.ReadAll(c.CACert) } // return the Jenkins API client - return client, nil + return &jenkinsAdapter{Jenkins: client} +} + +func (j *jenkinsAdapter) Credentials() *jenkins.CredentialsManager { + return &jenkins.CredentialsManager{ + J: j.Jenkins, + } } diff --git a/jenkins/config_test.go b/jenkins/config_test.go index ae4abdf..adc7014 100644 --- a/jenkins/config_test.go +++ b/jenkins/config_test.go @@ -1,10 +1,24 @@ package jenkins -import jenkins "github.com/bndr/gojenkins" +import ( + "bytes" + "testing" + + jenkins "github.com/bndr/gojenkins" +) type mockJenkinsClient struct { - mockDeleteJob func(name string) (bool, error) - mockGetJob func(id string, parentIDs ...string) (*jenkins.Job, error) + mockCreateJobInFolder func(config string, jobName string, parentIDs ...string) (*jenkins.Job, error) + mockDeleteJob func(name string) (bool, error) + mockGetJob func(id string, parentIDs ...string) (*jenkins.Job, error) +} + +func (m *mockJenkinsClient) CreateJobInFolder(config string, jobName string, parentIDs ...string) (*jenkins.Job, error) { + return m.mockCreateJobInFolder(config, jobName, parentIDs...) +} + +func (m *mockJenkinsClient) Credentials() *jenkins.CredentialsManager { + return &jenkins.CredentialsManager{} } func (m *mockJenkinsClient) DeleteJob(name string) (bool, error) { @@ -14,3 +28,28 @@ func (m *mockJenkinsClient) DeleteJob(name string) (bool, error) { func (m *mockJenkinsClient) GetJob(id string, parentIDs ...string) (*jenkins.Job, error) { return m.mockGetJob(id, parentIDs...) } + +func TestNewJenkinsClient(t *testing.T) { + c := newJenkinsClient(&Config{}) + if c == nil { + t.Errorf("Expected populated client") + } + + c = newJenkinsClient(&Config{ + CACert: bytes.NewBufferString("certificate"), + }) + if string(c.Requester.CACert) != "certificate" { + t.Errorf("Initialization did not extract certificate data") + } +} + +func TestJenkinsAdapter_Credentials(t *testing.T) { + c := newJenkinsClient(&Config{}) + cm := c.Credentials() + + if cm == nil { + t.Errorf("Expected populated client") + } else if cm.J != c.Jenkins { + t.Error("Expected credentials client to match client") + } +} diff --git a/jenkins/provider.go b/jenkins/provider.go index 1f19ce8..8724d20 100644 --- a/jenkins/provider.go +++ b/jenkins/provider.go @@ -2,6 +2,7 @@ package jenkins import ( "context" + "os" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -49,13 +50,21 @@ func Provider() *schema.Provider { func configureProvider(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { config := Config{ ServerURL: d.Get("server_url").(string), - CACert: d.Get("ca_cert").(string), Username: d.Get("username").(string), Password: d.Get("password").(string), } - client, err := newJenkinsClient(&config) - if err != nil { + // Read the certificate + var err error + if d.Get("ca_cert").(string) != "" { + config.CACert, err = os.Open(d.Get("ca_cert").(string)) + if err != nil { + return nil, diag.Errorf("Unable to open certificate file %s: %s", d.Get("ca_cert").(string), err.Error()) + } + } + + client := newJenkinsClient(&config) + if _, err = client.Init(); err != nil { return nil, diag.FromErr(err) } diff --git a/jenkins/resource_jenkins_folder_test.go b/jenkins/resource_jenkins_folder_test.go index e01586a..59bd52f 100644 --- a/jenkins/resource_jenkins_folder_test.go +++ b/jenkins/resource_jenkins_folder_test.go @@ -4,7 +4,6 @@ import ( "fmt" "testing" - jenkins "github.com/bndr/gojenkins" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -26,7 +25,7 @@ func TestAccJenkinsFolder_basic(t *testing.T) { } func testAccCheckJenkinsFolderDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*jenkins.Jenkins) + client := testAccProvider.Meta().(jenkinsClient) for _, rs := range s.RootModule().Resources { if rs.Type != "jenkins_folder" { diff --git a/jenkins/resource_jenkins_job.go b/jenkins/resource_jenkins_job.go index 1a3ce67..7851f73 100644 --- a/jenkins/resource_jenkins_job.go +++ b/jenkins/resource_jenkins_job.go @@ -6,7 +6,6 @@ import ( "log" "strings" - jenkins "github.com/bndr/gojenkins" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -41,7 +40,7 @@ func resourceJenkinsJob() *schema.Resource { } func resourceJenkinsJobCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*jenkins.Jenkins) + client := meta.(jenkinsClient) name := formatJobName(d.Get("name").(string)) baseName, folders := parseJobName(d.Get("name").(string)) @@ -92,7 +91,7 @@ func resourceJenkinsJobRead(ctx context.Context, d *schema.ResourceData, meta in } func resourceJenkinsJobUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*jenkins.Jenkins) + client := meta.(jenkinsClient) name := d.Id() // grab job by current name diff --git a/jenkins/resource_jenkins_job_test.go b/jenkins/resource_jenkins_job_test.go index a4b05e2..a8e1a1e 100644 --- a/jenkins/resource_jenkins_job_test.go +++ b/jenkins/resource_jenkins_job_test.go @@ -31,7 +31,7 @@ func TestAccJenkinsJob_basic(t *testing.T) { } func testAccCheckJenkinsJobDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*jenkins.Jenkins) + client := testAccProvider.Meta().(jenkinsClient) for _, rs := range s.RootModule().Resources { if rs.Type != "jenkins_job" {