mirror of
https://github.com/appleboy/drone-jenkins.git
synced 2026-06-04 10:15:02 +08:00
767 lines
20 KiB
Go
767 lines
20 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestParseJobPath(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "appleboy",
|
|
Token: "1234",
|
|
}
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"http://example.com",
|
|
"",
|
|
false,
|
|
"",
|
|
false,
|
|
)
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, "/job/foo", jenkins.parseJobPath("/foo/"))
|
|
assert.Equal(t, "/job/foo", jenkins.parseJobPath("foo/"))
|
|
assert.Equal(t, "/job/foo/job/bar", jenkins.parseJobPath("foo/bar"))
|
|
assert.Equal(t, "/job/foo/job/bar", jenkins.parseJobPath("foo///bar"))
|
|
}
|
|
|
|
func TestUnSupportProtocol(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "foo",
|
|
Token: "bar",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, "example.com", "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
queueID, err := jenkins.trigger(context.Background(), "drone-jenkins", nil)
|
|
assert.NotNil(t, err)
|
|
assert.Equal(t, 0, queueID)
|
|
}
|
|
|
|
func TestTriggerBuild(t *testing.T) {
|
|
// Create a mock Jenkins server
|
|
var receivedParams url.Values
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
receivedParams = r.URL.Query()
|
|
w.Header().Set("Location", "http://jenkins.example.com/queue/item/123/")
|
|
w.WriteHeader(http.StatusCreated)
|
|
}))
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "foo",
|
|
Token: "bar",
|
|
}
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
server.URL,
|
|
"remote-token",
|
|
false,
|
|
"",
|
|
false,
|
|
)
|
|
assert.NoError(t, err)
|
|
|
|
params := url.Values{"param": []string{"value"}}
|
|
queueID, err := jenkins.trigger(context.Background(), "drone-jenkins", params)
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 123, queueID)
|
|
assert.Equal(t, "value", receivedParams.Get("param"))
|
|
assert.Equal(t, "remote-token", receivedParams.Get("token"))
|
|
}
|
|
|
|
func TestPostAndGetLocation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
location string
|
|
expectID int
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "valid location with trailing slash",
|
|
location: "http://jenkins.example.com/queue/item/456/",
|
|
expectID: 456,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid location without trailing slash",
|
|
location: "http://jenkins.example.com/queue/item/789",
|
|
expectID: 789,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "no location header",
|
|
location: "",
|
|
expectID: 0,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid location format",
|
|
location: "http://jenkins.example.com/invalid/path",
|
|
expectID: 0,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(
|
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if tt.location != "" {
|
|
w.Header().Set("Location", tt.location)
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
}),
|
|
)
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
queueID, err := jenkins.postAndGetLocation(context.Background(), "/test", nil)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.expectID, queueID)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetQueueItem(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
queueID int
|
|
responseBody string
|
|
responseStatus int
|
|
expectError bool
|
|
expectBlocked bool
|
|
expectBuildNum int
|
|
}{
|
|
{
|
|
name: "queue item with build number",
|
|
queueID: 123,
|
|
responseBody: `{"id":123,"blocked":false,"buildable":true,` +
|
|
`"executable":{"number":456,"url":"http://jenkins.example.com/job/test/456/"}}`,
|
|
responseStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectBlocked: false,
|
|
expectBuildNum: 456,
|
|
},
|
|
{
|
|
name: "queue item waiting",
|
|
queueID: 124,
|
|
responseBody: `{"id":124,"blocked":false,"buildable":true,"why":"Waiting for executor"}`,
|
|
responseStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectBlocked: false,
|
|
expectBuildNum: 0,
|
|
},
|
|
{
|
|
name: "queue item blocked",
|
|
queueID: 125,
|
|
responseBody: `{"id":125,"blocked":true,"buildable":false,"why":"Blocked by other job"}`,
|
|
responseStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectBlocked: true,
|
|
expectBuildNum: 0,
|
|
},
|
|
{
|
|
name: "queue item not found",
|
|
queueID: 999,
|
|
responseBody: "Not Found",
|
|
responseStatus: http.StatusNotFound,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(
|
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Contains(t, r.URL.Path, "/queue/item/")
|
|
w.WriteHeader(tt.responseStatus)
|
|
_, _ = w.Write([]byte(tt.responseBody))
|
|
}),
|
|
)
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
queueItem, err := jenkins.getQueueItem(context.Background(), tt.queueID)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, queueItem)
|
|
assert.Equal(t, tt.queueID, queueItem.ID)
|
|
assert.Equal(t, tt.expectBlocked, queueItem.Blocked)
|
|
if queueItem.Executable != nil {
|
|
assert.Equal(t, tt.expectBuildNum, queueItem.Executable.Number)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetBuildInfo(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
jobName string
|
|
buildNumber int
|
|
responseBody string
|
|
responseStatus int
|
|
expectError bool
|
|
expectBuilding bool
|
|
expectResult string
|
|
}{
|
|
{
|
|
name: "build in progress",
|
|
jobName: "test-job",
|
|
buildNumber: 123,
|
|
responseBody: `{"number":123,"building":true,"duration":0,"result":null,` +
|
|
`"url":"http://jenkins.example.com/job/test-job/123/"}`,
|
|
responseStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectBuilding: true,
|
|
expectResult: "",
|
|
},
|
|
{
|
|
name: "build completed successfully",
|
|
jobName: "test-job",
|
|
buildNumber: 124,
|
|
responseBody: `{"number":124,"building":false,"duration":5000,"result":"SUCCESS",` +
|
|
`"url":"http://jenkins.example.com/job/test-job/124/"}`,
|
|
responseStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectBuilding: false,
|
|
expectResult: "SUCCESS",
|
|
},
|
|
{
|
|
name: "build failed",
|
|
jobName: "test-job",
|
|
buildNumber: 125,
|
|
responseBody: `{"number":125,"building":false,"duration":3000,"result":"FAILURE",` +
|
|
`"url":"http://jenkins.example.com/job/test-job/125/"}`,
|
|
responseStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectBuilding: false,
|
|
expectResult: "FAILURE",
|
|
},
|
|
{
|
|
name: "build not found",
|
|
jobName: "test-job",
|
|
buildNumber: 999,
|
|
responseBody: "Not Found",
|
|
responseStatus: http.StatusNotFound,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(
|
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Contains(t, r.URL.Path, "/job/")
|
|
w.WriteHeader(tt.responseStatus)
|
|
_, _ = w.Write([]byte(tt.responseBody))
|
|
}),
|
|
)
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
buildInfo, err := jenkins.getBuildInfo(context.Background(), tt.jobName, tt.buildNumber)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, buildInfo)
|
|
assert.Equal(t, tt.buildNumber, buildInfo.Number)
|
|
assert.Equal(t, tt.expectBuilding, buildInfo.Building)
|
|
assert.Equal(t, tt.expectResult, buildInfo.Result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWaitForCompletion(t *testing.T) {
|
|
t.Run("successful completion", func(t *testing.T) {
|
|
var callCount int32
|
|
queueID := 123
|
|
buildNumber := 456
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
count := atomic.AddInt32(&callCount, 1)
|
|
|
|
switch r.URL.Path {
|
|
case testQueueItemPath:
|
|
// First call: queue item without build number
|
|
// Second call: queue item with build number
|
|
w.WriteHeader(http.StatusOK)
|
|
if count == 1 {
|
|
_, _ = w.Write([]byte(
|
|
`{"id":123,"blocked":false,"buildable":true,"why":"Waiting for executor"}`,
|
|
))
|
|
} else {
|
|
_, _ = w.Write([]byte(`{"id":123,"blocked":false,"buildable":true,` +
|
|
`"executable":{"number":456,"url":"http://example.com/job/test/456/"}}`))
|
|
}
|
|
case testBuildStatusPath:
|
|
// First call: build in progress
|
|
// Second call: build completed
|
|
w.WriteHeader(http.StatusOK)
|
|
if count <= 3 {
|
|
_, _ = w.Write(
|
|
[]byte(`{"number":456,"building":true,"duration":0,"result":null}`),
|
|
)
|
|
} else {
|
|
_, _ = w.Write(
|
|
[]byte(
|
|
`{"number":456,"building":false,"duration":5000,"result":"SUCCESS"}`,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
buildInfo, err := jenkins.waitForCompletion(
|
|
context.Background(),
|
|
"test-job",
|
|
queueID,
|
|
100*time.Millisecond,
|
|
5*time.Second,
|
|
)
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, buildInfo)
|
|
assert.Equal(t, buildNumber, buildInfo.Number)
|
|
assert.False(t, buildInfo.Building)
|
|
assert.Equal(t, "SUCCESS", buildInfo.Result)
|
|
})
|
|
|
|
t.Run("timeout waiting for queue", func(t *testing.T) {
|
|
queueID := 123
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Always return queue item without build number
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(
|
|
[]byte(`{"id":123,"blocked":false,"buildable":true,"why":"Waiting forever"}`),
|
|
)
|
|
}))
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
buildInfo, err := jenkins.waitForCompletion(
|
|
context.Background(),
|
|
"test-job",
|
|
queueID,
|
|
50*time.Millisecond,
|
|
200*time.Millisecond,
|
|
)
|
|
|
|
assert.Error(t, err)
|
|
assert.Nil(t, buildInfo)
|
|
assert.Contains(t, err.Error(), "timeout")
|
|
})
|
|
|
|
t.Run("timeout waiting for build", func(t *testing.T) {
|
|
var callCount int32
|
|
queueID := 123
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
count := atomic.AddInt32(&callCount, 1)
|
|
|
|
switch r.URL.Path {
|
|
case testQueueItemPath:
|
|
// Return build number immediately
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(`{"id":123,"blocked":false,"buildable":true,` +
|
|
`"executable":{"number":456,"url":"http://example.com/job/test/456/"}}`))
|
|
case testBuildStatusPath:
|
|
// Always return building status
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(`{"number":456,"building":true,"duration":0,"result":null}`))
|
|
}
|
|
_ = count
|
|
}))
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
buildInfo, err := jenkins.waitForCompletion(
|
|
context.Background(),
|
|
"test-job",
|
|
queueID,
|
|
50*time.Millisecond,
|
|
200*time.Millisecond,
|
|
)
|
|
|
|
assert.Error(t, err)
|
|
assert.Nil(t, buildInfo)
|
|
assert.Contains(t, err.Error(), "timeout")
|
|
})
|
|
|
|
t.Run("build failed", func(t *testing.T) {
|
|
var callCount int32
|
|
queueID := 123
|
|
buildNumber := 456
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
count := atomic.AddInt32(&callCount, 1)
|
|
|
|
switch r.URL.Path {
|
|
case testQueueItemPath:
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(`{"id":123,"blocked":false,"buildable":true,` +
|
|
`"executable":{"number":456,"url":"http://example.com/job/test/456/"}}`))
|
|
case testBuildStatusPath:
|
|
// First call: building, second call: failed
|
|
w.WriteHeader(http.StatusOK)
|
|
if count == 1 {
|
|
_, _ = w.Write(
|
|
[]byte(`{"number":456,"building":true,"duration":0,"result":null}`),
|
|
)
|
|
} else {
|
|
_, _ = w.Write(
|
|
[]byte(
|
|
`{"number":456,"building":false,"duration":3000,"result":"FAILURE"}`,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
|
|
assert.NoError(t, err)
|
|
|
|
buildInfo, err := jenkins.waitForCompletion(
|
|
context.Background(),
|
|
"test-job",
|
|
queueID,
|
|
50*time.Millisecond,
|
|
5*time.Second,
|
|
)
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, buildInfo)
|
|
assert.Equal(t, buildNumber, buildInfo.Number)
|
|
assert.False(t, buildInfo.Building)
|
|
assert.Equal(t, "FAILURE", buildInfo.Result)
|
|
})
|
|
}
|
|
|
|
// Sample CA certificate for testing (self-signed, not for production use)
|
|
const testCACert = `-----BEGIN CERTIFICATE-----
|
|
MIIDAzCCAeugAwIBAgIUGYBGBr+t20UAWJorEPULxzGIXUEwDQYJKoZIhvcNAQEL
|
|
BQAwETEPMA0GA1UEAwwGdGVzdGNhMB4XDTI1MTIwNjA1MDgzMloXDTM1MTIwNDA1
|
|
MDgzMlowETEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
|
MIIBCgKCAQEAq4bwnABqFenRVUoHLKhPiJXkh6TBFUaCWiEpKYNPywptBJNdyWNf
|
|
ouDxJ8gvQOMCkp3trnAHFcT6W5s8QLM1Hf/70QZI9GU/BtYm0KijU8aM+GJawNto
|
|
sK103TeCd0tVenDkxfamBGYnh3L5jtk0V/jeIsAIfFoe9Citu3MttRfxnSmZ4w2K
|
|
qlS14vKhFlO4WrXAh9j4PaVE5DL7jya/UKe6VVQIONCwUipRN6nU3UXhR7akVSmF
|
|
/bYkFsfdcErXJHjDpg+0xOsa5LJhzRkx5Uoqtviq2oRVVYhZc0eTwjq/407ocJ25
|
|
6WmerfKrtFDpzOZPa4XPVX9Am4vWugtrwQIDAQABo1MwUTAdBgNVHQ4EFgQUh7kL
|
|
LqmsvQP3TI6eiLVK7Gs7A00wHwYDVR0jBBgwFoAUh7kLLqmsvQP3TI6eiLVK7Gs7
|
|
A00wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEApLOdWacya+Zi
|
|
w0Fd3UfSveuRsayAkMkZ4p0L9XKlADzwKtSF1Ykn6wiEiYfXd2TvffsR2XglOXFc
|
|
181IpBhP5u2mzK6pRvH9mqTs3w8JTcXMFmg8AKE2Vg5k22tBM2OUJJgKXkiACuHS
|
|
pZeOOvJcnjGunbTRwqais0TLYnkOcFsbgrSBKv82HiVootH/iKZahf1ViFMOURTh
|
|
MqjwIous7Y53Rq4RmfycIjNwODlDW0i5atKe8incDBiIYKw6sH8WN+nuhnHC/vJ5
|
|
5ZQvGCUsGOvma5ojWAiLs8wu4dODuF5ZNID3t+M36PQs7JDaQNN+AkZROOTSMqa/
|
|
ud3vS1A5+g==
|
|
-----END CERTIFICATE-----`
|
|
|
|
func TestLoadCACert(t *testing.T) {
|
|
t.Run("empty string returns nil", func(t *testing.T) {
|
|
data, err := loadCACert(context.Background(), "")
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, data)
|
|
})
|
|
|
|
t.Run("PEM content directly", func(t *testing.T) {
|
|
data, err := loadCACert(context.Background(), testCACert)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, data)
|
|
assert.Contains(t, string(data), "BEGIN CERTIFICATE")
|
|
})
|
|
|
|
t.Run("PEM content with leading whitespace", func(t *testing.T) {
|
|
data, err := loadCACert(context.Background(), " \n"+testCACert)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, data)
|
|
assert.Contains(t, string(data), "BEGIN CERTIFICATE")
|
|
})
|
|
|
|
t.Run("file path", func(t *testing.T) {
|
|
// Create a temporary file with the certificate
|
|
tmpDir := t.TempDir()
|
|
certFile := filepath.Join(tmpDir, "ca.pem")
|
|
err := os.WriteFile(certFile, []byte(testCACert), 0o600)
|
|
assert.NoError(t, err)
|
|
|
|
data, err := loadCACert(context.Background(), certFile)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, data)
|
|
assert.Contains(t, string(data), "BEGIN CERTIFICATE")
|
|
})
|
|
|
|
t.Run("file not found", func(t *testing.T) {
|
|
data, err := loadCACert(context.Background(), "/nonexistent/path/ca.pem")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, data)
|
|
assert.Contains(t, err.Error(), "failed to read CA certificate file")
|
|
})
|
|
|
|
t.Run("HTTP URL", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(testCACert))
|
|
}))
|
|
defer server.Close()
|
|
|
|
data, err := loadCACert(context.Background(), server.URL)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, data)
|
|
assert.Contains(t, string(data), "BEGIN CERTIFICATE")
|
|
})
|
|
|
|
t.Run("HTTPS URL", func(t *testing.T) {
|
|
server := httptest.NewTLSServer(
|
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(testCACert))
|
|
}),
|
|
)
|
|
defer server.Close()
|
|
|
|
// Note: This test uses the test server's self-signed cert
|
|
// In real scenarios, the URL would be to a trusted source
|
|
// We skip HTTPS verification for this test
|
|
data, err := loadCACert(context.Background(), server.URL)
|
|
// This may fail due to certificate verification, which is expected
|
|
if err != nil {
|
|
assert.Contains(t, err.Error(), "certificate")
|
|
} else {
|
|
assert.NotNil(t, data)
|
|
}
|
|
})
|
|
|
|
t.Run("HTTP URL returns error status", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}))
|
|
defer server.Close()
|
|
|
|
data, err := loadCACert(context.Background(), server.URL)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, data)
|
|
assert.Contains(t, err.Error(), "HTTP 404")
|
|
})
|
|
|
|
t.Run("HTTP URL unreachable", func(t *testing.T) {
|
|
data, err := loadCACert(context.Background(), "http://localhost:59999/nonexistent")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, data)
|
|
assert.Contains(t, err.Error(), "failed to fetch CA certificate from URL")
|
|
})
|
|
}
|
|
|
|
func TestNewJenkinsWithCACert(t *testing.T) {
|
|
t.Run("with valid CA certificate", func(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"https://example.com",
|
|
"",
|
|
false,
|
|
testCACert,
|
|
false,
|
|
)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, jenkins)
|
|
assert.NotNil(t, jenkins.Client)
|
|
})
|
|
|
|
t.Run("with CA certificate from file", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
certFile := filepath.Join(tmpDir, "ca.pem")
|
|
err := os.WriteFile(certFile, []byte(testCACert), 0o600)
|
|
assert.NoError(t, err)
|
|
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"https://example.com",
|
|
"",
|
|
false,
|
|
certFile,
|
|
false,
|
|
)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, jenkins)
|
|
})
|
|
|
|
t.Run("with invalid CA certificate content", func(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"https://example.com",
|
|
"",
|
|
false,
|
|
"invalid-cert-data",
|
|
false,
|
|
)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, jenkins)
|
|
assert.Contains(t, err.Error(), "failed to read CA certificate file")
|
|
})
|
|
|
|
t.Run("with invalid PEM format", func(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
invalidPEM := "-----BEGIN CERTIFICATE-----\ninvalid-base64-data\n-----END CERTIFICATE-----"
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"https://example.com",
|
|
"",
|
|
false,
|
|
invalidPEM,
|
|
false,
|
|
)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, jenkins)
|
|
assert.Contains(t, err.Error(), "failed to parse CA certificate")
|
|
})
|
|
|
|
t.Run("with nonexistent file path", func(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"https://example.com",
|
|
"",
|
|
false,
|
|
"/nonexistent/ca.pem",
|
|
false,
|
|
)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, jenkins)
|
|
assert.Contains(t, err.Error(), "failed to load CA certificate")
|
|
})
|
|
|
|
t.Run("insecure flag takes precedence over CA cert", func(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
// When insecure is true, CA cert should be ignored
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"https://example.com",
|
|
"",
|
|
true,
|
|
testCACert,
|
|
false,
|
|
)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, jenkins)
|
|
})
|
|
|
|
t.Run("without CA certificate uses default client", func(t *testing.T) {
|
|
auth := &Auth{
|
|
Username: "test",
|
|
Token: "test",
|
|
}
|
|
jenkins, err := NewJenkins(
|
|
context.Background(),
|
|
auth,
|
|
"https://example.com",
|
|
"",
|
|
false,
|
|
"",
|
|
false,
|
|
)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, jenkins)
|
|
assert.NotNil(t, jenkins.Client)
|
|
// Client should have CookieJar for CSRF session management
|
|
assert.NotNil(t, jenkins.Client.Jar)
|
|
})
|
|
}
|