Compare commits

..

16 Commits

Author SHA1 Message Date
Bo-Yi Wu f88736ff6f ci(actions): bump codecov-action from v6 to v7
- Update codecov/codecov-action from v6 to v7
2026-06-13 15:46:18 +08:00
Bo-Yi Wu e1985fadc9 refactor: extract repeated string literals into constants
- Add tokenParam const in jenkins.go and reuse across main.go
- Add shared test_helpers_test.go with test constants
- Remove unused //nolint:gosec directive in jenkins.go
- Resolve golangci-lint v2.12 goconst and nolintlint warnings
2026-05-08 22:49:00 +08:00
Bo-Yi Wu 45a9d76c71 chore: bump go directive to 1.25.10
- Update go.mod go directive from 1.25.9 to 1.25.10
2026-05-08 21:19:20 +08:00
Bo-Yi Wu 5ac640a972 ci: bump golangci-lint to v2.12
- Upgrade golangci-lint version from v2.11 to v2.12
2026-05-08 20:13:39 +08:00
Bo-Yi Wu 24ef1dc20c ci(actions): bump trivy-action to v0.36.0 and codecov-action to v6 2026-04-25 16:51:03 +08:00
Bo-Yi Wu 2e4860b70c ci(docker): fail push when trivy finds CRITICAL/HIGH issues 2026-04-16 23:01:05 +08:00
Bo-Yi Wu c885f9c805 ci: enable check-latest in docker and goreleaser workflows 2026-04-16 22:42:47 +08:00
Bo-Yi Wu 764f7b6bf6 fix: skip integration tests without telegram secrets; apply modernize fix 2026-04-16 22:39:41 +08:00
Bo-Yi Wu c8d19e8231 ci: enable check-latest for setup-go to fetch newest patch 2026-04-16 21:15:40 +08:00
Bo-Yi Wu c25c40af3b ci: pin golangci-lint to v2.11 2026-04-16 21:11:16 +08:00
Bo-Yi Wu e0116d31de ci: bump GitHub Actions and add Go 1.25/1.26 to test matrix 2026-04-16 21:03:25 +08:00
Bo-Yi Wu c2d73374b4 chore: bump go directive to 1.25.9 2026-04-16 20:57:55 +08:00
Bo-Yi Wu c773b54f0e ci: standardize Trivy security scanning workflows
- Add Trivy image scan job to trivy.yml alongside existing repo scan
- Add Trivy image scan step in docker.yml before pushing Docker image
- Add security-events permission for SARIF upload
2026-04-16 18:10:08 +08:00
Bo-Yi Wu 5d50e1e745 ci(actions): upgrade GitHub Actions to latest versions
- bump actions/checkout to v6
- bump actions/setup-go to v6
- bump actions/cache to v5
- bump goreleaser/goreleaser-action to v7
- bump golangci/golangci-lint-action to v9
- bump github/codeql-action/* to v4
- bump codecov/codecov-action to v5
- bump docker/build-push-action to v7
- bump docker/login-action to v4
- bump docker/metadata-action to v6
- bump docker/setup-buildx-action to v4
- bump docker/setup-qemu-action to v4
- bump hadolint/hadolint-action to v3.3.0
- bump aquasecurity/trivy-action to v0.35.0
2026-04-16 12:06:52 +08:00
appleboy f2a83d3d6c docs: document Jenkins authentication and CSRF protection methods
- Clarify and expand Jenkins authentication documentation, outlining API token, remote trigger token, and combined authentication options
- Add detailed explanations and requirements for working with CSRF protection in Jenkins, including error guidance
- Modify authentication requirements and usage examples to emphasize API token over remote trigger token, especially for secure Jenkins setups
- Introduce a Troubleshooting section with solutions for common Jenkins authentication errors (403, 401, remote token issues)
- Add corresponding CSRF protection notices and troubleshooting guidance to Chinese (Simplified and Traditional) documentation

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-12-27 11:56:15 +08:00
Bo-Yi Wu 351ac33e2d feat: refactor authentication logic and broaden test coverage (#50)
* feat: refactor authentication logic and broaden test coverage

- Improve authentication checks to only require username and token when both are provided
- Update validation logic to allow either (username and token) or remote-token for authentication
- Enhance test coverage for various authentication scenarios
- Refine error messages to indicate a generic authentication requirement instead of specifying missing username or token

Signed-off-by: appleboy <appleboy.tw@gmail.com>

* style: streamline authentication error handling in config validation

- Simplify authentication error message in config validation

Signed-off-by: appleboy <appleboy.tw@gmail.com>

---------

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-12-27 11:25:36 +08:00
13 changed files with 405 additions and 150 deletions
+36 -5
View File
@@ -10,6 +10,11 @@ on:
branches: branches:
- "master" - "master"
permissions:
contents: read
packages: write
security-events: write
jobs: jobs:
build-docker: build-docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -18,6 +23,7 @@ jobs:
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: "^1" go-version: "^1"
check-latest: true
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
@@ -29,13 +35,13 @@ jobs:
make build_linux_arm64 make build_linux_arm64
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@@ -43,7 +49,7 @@ jobs:
- name: Docker meta - name: Docker meta
id: docker-meta id: docker-meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: | images: |
ghcr.io/${{ github.repository }} ghcr.io/${{ github.repository }}
@@ -53,8 +59,33 @@ jobs:
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
- name: Build image for scanning
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: drone-jenkins:scan
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: "drone-jenkins:scan"
format: "sarif"
output: "trivy-image-results.sarif"
severity: "CRITICAL,HIGH"
exit-code: '1'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: "trivy-image-results.sarif"
category: "trivy-docker-image"
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v7
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
check-latest: true check-latest: true
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6 uses: goreleaser/goreleaser-action@v7
with: with:
# either 'goreleaser' (default) or 'goreleaser-pro' # either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser distribution: goreleaser
+5 -3
View File
@@ -12,13 +12,14 @@ jobs:
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: "stable" go-version: "stable"
check-latest: true
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Setup golangci-lint - name: Setup golangci-lint
uses: golangci/golangci-lint-action@v9 uses: golangci/golangci-lint-action@v9
with: with:
version: v2.6 version: v2.12
args: --verbose args: --verbose
- uses: hadolint/hadolint-action@v3.3.0 - uses: hadolint/hadolint-action@v3.3.0
@@ -30,7 +31,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
go: ["1.25"] go: ["1.25", "1.26"]
include: include:
- os: ubuntu-latest - os: ubuntu-latest
go-build: ~/.cache/go-build go-build: ~/.cache/go-build
@@ -44,6 +45,7 @@ jobs:
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
check-latest: true
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v6 uses: actions/checkout@v6
@@ -63,6 +65,6 @@ jobs:
go test -race -cover -coverprofile=coverage.out ./... go test -race -cover -coverprofile=coverage.out ./...
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v7
with: with:
flags: ${{ matrix.os }},go-${{ matrix.go }} flags: ${{ matrix.os }},go-${{ matrix.go }}
+53 -24
View File
@@ -10,47 +10,76 @@ on:
schedule: schedule:
# Run daily at 00:00 UTC # Run daily at 00:00 UTC
- cron: "0 0 * * *" - cron: "0 0 * * *"
workflow_dispatch: # Allow manual trigger workflow_dispatch:
permissions: permissions:
contents: read contents: read
security-events: write # Required for uploading SARIF results security-events: write
jobs: jobs:
trivy-scan: trivy-repo-scan:
name: Trivy Security Scan name: Trivy Repository Scan
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Run Trivy vulnerability scanner (source code) - name: Run Trivy vulnerability scanner (repo)
uses: aquasecurity/trivy-action@0.33.1 uses: aquasecurity/trivy-action@v0.36.0
with: with:
scan-type: "fs" scan-type: "fs"
scan-ref: "." scan-ref: "."
scanners: "vuln,secret,misconfig"
format: "sarif" format: "sarif"
output: "trivy-results.sarif" output: "trivy-repo-results.sarif"
severity: "CRITICAL,HIGH,MEDIUM" severity: "CRITICAL,HIGH"
ignore-unfixed: true
- name: Upload Trivy results to GitHub Security tab - name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4 uses: github/codeql-action/upload-sarif@v4
if: always() if: always()
with: with:
sarif_file: "trivy-results.sarif" sarif_file: "trivy-repo-results.sarif"
- name: Run Trivy scanner (table output for logs) trivy-image-scan:
uses: aquasecurity/trivy-action@0.33.1 name: Trivy Image Scan
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
- name: Build binary
run: |
make build_linux_amd64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build Docker image for scanning
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: drone-jenkins:scan
- name: Run Trivy vulnerability scanner (image)
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: "drone-jenkins:scan"
format: "sarif"
output: "trivy-image-results.sarif"
severity: "CRITICAL,HIGH"
- name: Upload Trivy image scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always() if: always()
with: with:
scan-type: "fs" sarif_file: "trivy-image-results.sarif"
scan-ref: "." category: "trivy-image"
scanners: "vuln,secret,misconfig"
format: "table"
severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true
exit-code: "1"
+145 -6
View File
@@ -46,10 +46,16 @@ Whether you're managing a hybrid CI/CD environment or orchestrating complex mult
- [Configuration](#configuration) - [Configuration](#configuration)
- [Jenkins Server Setup](#jenkins-server-setup) - [Jenkins Server Setup](#jenkins-server-setup)
- [Authentication](#authentication) - [Authentication](#authentication)
- [Understanding Jenkins Authentication](#understanding-jenkins-authentication)
- [CSRF Protection Notice](#csrf-protection-notice)
- [Parameters Reference](#parameters-reference) - [Parameters Reference](#parameters-reference)
- [Usage](#usage) - [Usage](#usage)
- [Command Line](#command-line) - [Command Line](#command-line)
- [Docker](#docker) - [Docker](#docker)
- [Troubleshooting](#troubleshooting)
- [Error: 403 No valid crumb was included in the request](#error-403-no-valid-crumb-was-included-in-the-request)
- [Error: 401 Unauthorized](#error-401-unauthorized)
- [Remote Token Not Working](#remote-token-not-working)
- [Development](#development) - [Development](#development)
- [Building](#building) - [Building](#building)
- [Testing](#testing) - [Testing](#testing)
@@ -125,7 +131,20 @@ docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --re
### Authentication ### Authentication
Jenkins API tokens are recommended for authentication. To create an API token: #### Understanding Jenkins Authentication
Jenkins supports multiple authentication methods for triggering builds. This tool supports two approaches:
**1. API Token Authentication (Recommended)**
Use Jenkins user credentials with an API token. This method:
- ✅ Works with all Jenkins configurations
- ✅ Supports CSRF protection (enabled by default in modern Jenkins)
- ✅ Supports wait mode to monitor build completion
- ✅ Provides full access to Jenkins API features
To create an API token:
1. Log into Jenkins 1. Log into Jenkins
2. Click on your username (top right) 2. Click on your username (top right)
@@ -136,7 +155,42 @@ Jenkins API tokens are recommended for authentication. To create an API token:
![personal token](./images/personal-token.png) ![personal token](./images/personal-token.png)
Alternatively, you can use a remote trigger token configured in your Jenkins job settings. **2. Remote Trigger Token Authentication**
Use a remote trigger token configured in your Jenkins job. **Important limitations**:
- ⚠️ **Does not work** with Jenkins CSRF protection enabled (default in modern Jenkins)
- ⚠️ Requires anonymous users to have read access to the job, OR
- ⚠️ Must be combined with API token authentication (see Combined Authentication below)
**3. Combined Authentication (Recommended for Remote Tokens)**
Use both API token and remote trigger token together:
- ✅ Works with CSRF protection enabled
- ✅ Provides double authentication security
- ✅ Supports all features including wait mode
```bash
drone-jenkins \
--host http://jenkins.example.com/ \
--user YOUR_USERNAME \
--token YOUR_API_TOKEN \
--remote-token YOUR_REMOTE_TOKEN \
--job my-jenkins-job
```
#### CSRF Protection Notice
Modern Jenkins installations have CSRF protection enabled by default. If you encounter errors like:
```txt
Error 403 No valid crumb was included in the request
```
This means your Jenkins has CSRF protection enabled. You **must** use API token authentication (option 1 or 3 above). Remote trigger token alone will not work.
For more information about Jenkins CSRF protection, see the [official Jenkins documentation](https://www.jenkins.io/doc/book/security/csrf-protection/).
### Parameters Reference ### Parameters Reference
@@ -155,10 +209,19 @@ Alternatively, you can use a remote trigger token configured in your Jenkins job
| Timeout | `--timeout` | `PLUGIN_TIMEOUT`, `JENKINS_TIMEOUT` | No | Maximum time to wait for job completion (default: 30m) | | Timeout | `--timeout` | `PLUGIN_TIMEOUT`, `JENKINS_TIMEOUT` | No | Maximum time to wait for job completion (default: 30m) |
| Debug | `--debug` | `PLUGIN_DEBUG`, `JENKINS_DEBUG` | No | Enable debug mode to show detailed parameter information (default: false) | | Debug | `--debug` | `PLUGIN_DEBUG`, `JENKINS_DEBUG` | No | Enable debug mode to show detailed parameter information (default: false) |
**Authentication Requirements**: You must provide either: **Authentication Requirements**:
- `user` + `token` (API token authentication), OR For Jenkins with **CSRF protection enabled** (default in modern Jenkins):
- `remote-token` (remote trigger token authentication)
- **Required**: `user` + `token` (API token authentication)
- **Optional**: `remote-token` (for additional security)
For Jenkins with **CSRF protection disabled** (not recommended):
- **Option 1**: `user` + `token` (API token authentication)
- **Option 2**: `remote-token` only (requires anonymous read access to job)
**Important**: If you encounter "403 No valid crumb" errors, you must use API token authentication (`user` + `token`).
**Parameters Format**: The `parameters` field accepts a multi-line string where each line contains one `key=value` pair: **Parameters Format**: The `parameters` field accepts a multi-line string where each line contains one `key=value` pair:
@@ -220,9 +283,22 @@ drone-jenkins \
--job my-jenkins-job --job my-jenkins-job
``` ```
**Using remote token authentication:** **Using combined authentication (API token + remote token - Recommended):**
```bash ```bash
drone-jenkins \
--host http://jenkins.example.com/ \
--user appleboy \
--token XXXXXXXX \
--remote-token REMOTE_TOKEN_HERE \
--job my-jenkins-job
```
**Using remote token only (only works without CSRF protection):**
```bash
# Note: This will fail if Jenkins has CSRF protection enabled
# You will get "403 No valid crumb" error
drone-jenkins \ drone-jenkins \
--host http://jenkins.example.com/ \ --host http://jenkins.example.com/ \
--remote-token REMOTE_TOKEN_HERE \ --remote-token REMOTE_TOKEN_HERE \
@@ -309,6 +385,18 @@ docker run --rm \
ghcr.io/appleboy/drone-jenkins ghcr.io/appleboy/drone-jenkins
``` ```
**With combined authentication (API token + remote token):**
```bash
docker run --rm \
-e JENKINS_URL=http://jenkins.example.com/ \
-e JENKINS_USER=appleboy \
-e JENKINS_TOKEN=xxxxxxx \
-e JENKINS_REMOTE_TOKEN=your_remote_token \
-e JENKINS_JOB=my-jenkins-job \
ghcr.io/appleboy/drone-jenkins
```
**Wait for job completion:** **Wait for job completion:**
```bash ```bash
@@ -360,6 +448,57 @@ docker run --rm \
For more detailed examples and advanced configurations, see [DOCS.md](DOCS.md). For more detailed examples and advanced configurations, see [DOCS.md](DOCS.md).
## Troubleshooting
### Error: 403 No valid crumb was included in the request
**Cause**: Your Jenkins server has CSRF protection enabled (this is the default in modern Jenkins). Learn more at the [Jenkins CSRF Protection documentation](https://www.jenkins.io/doc/book/security/csrf-protection/).
**Solution**: Use API token authentication instead of remote token only:
```bash
# ❌ This will fail with CSRF protection enabled
drone-jenkins \
--host http://jenkins.example.com/ \
--remote-token YOUR_REMOTE_TOKEN \
--job my-jenkins-job
# ✅ Use this instead
drone-jenkins \
--host http://jenkins.example.com/ \
--user YOUR_USERNAME \
--token YOUR_API_TOKEN \
--job my-jenkins-job
# ✅ Or combine both for additional security
drone-jenkins \
--host http://jenkins.example.com/ \
--user YOUR_USERNAME \
--token YOUR_API_TOKEN \
--remote-token YOUR_REMOTE_TOKEN \
--job my-jenkins-job
```
### Error: 401 Unauthorized
**Cause**: Invalid credentials or incorrect authentication method.
**Solutions**:
1. Verify your username and API token are correct
2. Ensure you're using an API token, not your Jenkins password
3. Check if you have permission to trigger the job
4. Make sure both `--user` and `--token` are provided together
### Remote Token Not Working
**Cause**: Remote trigger tokens alone only work in specific scenarios:
- Jenkins has CSRF protection disabled (not recommended), AND
- Anonymous users have read access to the job
**Solution**: Use combined authentication (API token + remote token) as shown in the examples above.
## Development ## Development
### Building ### Building
+13
View File
@@ -46,6 +46,7 @@
- [配置](#配置) - [配置](#配置)
- [Jenkins 服务器设置](#jenkins-服务器设置) - [Jenkins 服务器设置](#jenkins-服务器设置)
- [认证](#认证) - [认证](#认证)
- [CSRF 保护注意事项](#csrf-保护注意事项)
- [参数参考](#参数参考) - [参数参考](#参数参考)
- [使用方式](#使用方式) - [使用方式](#使用方式)
- [命令行](#命令行) - [命令行](#命令行)
@@ -138,6 +139,18 @@ docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --re
或者,您可以使用在 Jenkins 任务设置中配置的远程触发令牌。 或者,您可以使用在 Jenkins 任务设置中配置的远程触发令牌。
#### CSRF 保护注意事项
现代 Jenkins 安装默认启用 CSRF 保护。如果您遇到以下错误:
```
Error 403 No valid crumb was included in the request
```
这表示您的 Jenkins 已启用 CSRF 保护。您**必须**使用 API 令牌认证(user + token)。单独使用远程触发令牌将无法工作。
如需更多关于 Jenkins CSRF 保护的信息,请参阅 [Jenkins 官方文档](https://www.jenkins.io/doc/book/security/csrf-protection/)。
### 参数参考 ### 参数参考
| 参数 | CLI 标志 | 环境变量 | 必需 | 说明 | | 参数 | CLI 标志 | 环境变量 | 必需 | 说明 |
+13
View File
@@ -46,6 +46,7 @@
- [設定](#設定) - [設定](#設定)
- [Jenkins 伺服器設定](#jenkins-伺服器設定) - [Jenkins 伺服器設定](#jenkins-伺服器設定)
- [認證](#認證) - [認證](#認證)
- [CSRF 保護注意事項](#csrf-保護注意事項)
- [參數參考](#參數參考) - [參數參考](#參數參考)
- [使用方式](#使用方式) - [使用方式](#使用方式)
- [命令列](#命令列) - [命令列](#命令列)
@@ -138,6 +139,18 @@ docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --re
或者,您可以使用在 Jenkins 任務設定中配置的遠端觸發令牌。 或者,您可以使用在 Jenkins 任務設定中配置的遠端觸發令牌。
#### CSRF 保護注意事項
現代 Jenkins 安裝預設啟用 CSRF 保護。如果您遇到以下錯誤:
```
Error 403 No valid crumb was included in the request
```
這表示您的 Jenkins 已啟用 CSRF 保護。您**必須**使用 API 令牌認證(user + token)。單獨使用遠端觸發令牌將無法運作。
如需更多關於 Jenkins CSRF 保護的資訊,請參閱 [Jenkins 官方文件](https://www.jenkins.io/doc/book/security/csrf-protection/)。
### 參數參考 ### 參數參考
| 參數 | CLI 旗標 | 環境變數 | 必要 | 說明 | | 參數 | CLI 旗標 | 環境變數 | 必要 | 說明 |
+1 -1
View File
@@ -1,6 +1,6 @@
module github.com/appleboy/drone-jenkins module github.com/appleboy/drone-jenkins
go 1.24.0 go 1.25.10
require ( require (
github.com/appleboy/com v1.1.1 github.com/appleboy/com v1.1.1
+5 -3
View File
@@ -19,6 +19,8 @@ import (
"github.com/yassinebenaid/godump" "github.com/yassinebenaid/godump"
) )
const tokenParam = "token"
type ( type (
// Auth contain username and token // Auth contain username and token
Auth struct { Auth struct {
@@ -487,14 +489,14 @@ func (jenkins *Jenkins) trigger(ctx context.Context, job string, params url.Valu
if params == nil { if params == nil {
params = url.Values{} params = url.Values{}
} }
params.Set("token", jenkins.Token) params.Set(tokenParam, jenkins.Token)
} }
var urlPath string var urlPath string
// Check if params contains build parameters (excluding 'token') // Check if params contains build parameters (excluding 'token')
hasBuildParams := false hasBuildParams := false
for key := range params { for key := range params {
if key != "token" { if key != tokenParam {
hasBuildParams = true hasBuildParams = true
break break
} }
@@ -524,7 +526,7 @@ func (jenkins *Jenkins) trigger(ctx context.Context, job string, params url.Valu
// Create a copy of params with masked token for display // Create a copy of params with masked token for display
displayParams := url.Values{} displayParams := url.Values{}
for key, values := range params { for key, values := range params {
if key == "token" { if key == tokenParam {
// Mask token values for security // Mask token values for security
displayParams[key] = []string{"***MASKED***"} displayParams[key] = []string{"***MASKED***"}
} else { } else {
+51 -43
View File
@@ -22,7 +22,7 @@ func TestParseJobPath(t *testing.T) {
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
context.Background(), context.Background(),
auth, auth,
"http://example.com", testExampleURL,
"", "",
false, false,
"", "",
@@ -38,8 +38,8 @@ func TestParseJobPath(t *testing.T) {
func TestUnSupportProtocol(t *testing.T) { func TestUnSupportProtocol(t *testing.T) {
auth := &Auth{ auth := &Auth{
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
} }
jenkins, err := NewJenkins(context.Background(), auth, "example.com", "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, "example.com", "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
@@ -60,8 +60,8 @@ func TestTriggerBuild(t *testing.T) {
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
} }
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
context.Background(), context.Background(),
@@ -129,8 +129,8 @@ func TestPostAndGetLocation(t *testing.T) {
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
@@ -206,8 +206,8 @@ func TestGetQueueItem(t *testing.T) {
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
@@ -242,7 +242,7 @@ func TestGetBuildInfo(t *testing.T) {
}{ }{
{ {
name: "build in progress", name: "build in progress",
jobName: "test-job", jobName: testJobName,
buildNumber: 123, buildNumber: 123,
responseBody: `{"number":123,"building":true,"duration":0,"result":null,` + responseBody: `{"number":123,"building":true,"duration":0,"result":null,` +
`"url":"http://jenkins.example.com/job/test-job/123/"}`, `"url":"http://jenkins.example.com/job/test-job/123/"}`,
@@ -253,7 +253,7 @@ func TestGetBuildInfo(t *testing.T) {
}, },
{ {
name: "build completed successfully", name: "build completed successfully",
jobName: "test-job", jobName: testJobName,
buildNumber: 124, buildNumber: 124,
responseBody: `{"number":124,"building":false,"duration":5000,"result":"SUCCESS",` + responseBody: `{"number":124,"building":false,"duration":5000,"result":"SUCCESS",` +
`"url":"http://jenkins.example.com/job/test-job/124/"}`, `"url":"http://jenkins.example.com/job/test-job/124/"}`,
@@ -264,7 +264,7 @@ func TestGetBuildInfo(t *testing.T) {
}, },
{ {
name: "build failed", name: "build failed",
jobName: "test-job", jobName: testJobName,
buildNumber: 125, buildNumber: 125,
responseBody: `{"number":125,"building":false,"duration":3000,"result":"FAILURE",` + responseBody: `{"number":125,"building":false,"duration":3000,"result":"FAILURE",` +
`"url":"http://jenkins.example.com/job/test-job/125/"}`, `"url":"http://jenkins.example.com/job/test-job/125/"}`,
@@ -275,7 +275,7 @@ func TestGetBuildInfo(t *testing.T) {
}, },
{ {
name: "build not found", name: "build not found",
jobName: "test-job", jobName: testJobName,
buildNumber: 999, buildNumber: 999,
responseBody: "Not Found", responseBody: "Not Found",
responseStatus: http.StatusNotFound, responseStatus: http.StatusNotFound,
@@ -295,8 +295,8 @@ func TestGetBuildInfo(t *testing.T) {
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
@@ -347,22 +347,26 @@ func TestWaitForCompletion(t *testing.T) {
[]byte(`{"number":456,"building":true,"duration":0,"result":null}`), []byte(`{"number":456,"building":true,"duration":0,"result":null}`),
) )
} else { } else {
_, _ = w.Write([]byte(`{"number":456,"building":false,"duration":5000,"result":"SUCCESS"}`)) _, _ = w.Write(
[]byte(
`{"number":456,"building":false,"duration":5000,"result":"SUCCESS"}`,
),
)
} }
} }
})) }))
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
buildInfo, err := jenkins.waitForCompletion( buildInfo, err := jenkins.waitForCompletion(
context.Background(), context.Background(),
"test-job", testJobName,
queueID, queueID,
100*time.Millisecond, 100*time.Millisecond,
5*time.Second, 5*time.Second,
@@ -388,15 +392,15 @@ func TestWaitForCompletion(t *testing.T) {
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
buildInfo, err := jenkins.waitForCompletion( buildInfo, err := jenkins.waitForCompletion(
context.Background(), context.Background(),
"test-job", testJobName,
queueID, queueID,
50*time.Millisecond, 50*time.Millisecond,
200*time.Millisecond, 200*time.Millisecond,
@@ -430,15 +434,15 @@ func TestWaitForCompletion(t *testing.T) {
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
buildInfo, err := jenkins.waitForCompletion( buildInfo, err := jenkins.waitForCompletion(
context.Background(), context.Background(),
"test-job", testJobName,
queueID, queueID,
50*time.Millisecond, 50*time.Millisecond,
200*time.Millisecond, 200*time.Millisecond,
@@ -470,22 +474,26 @@ func TestWaitForCompletion(t *testing.T) {
[]byte(`{"number":456,"building":true,"duration":0,"result":null}`), []byte(`{"number":456,"building":true,"duration":0,"result":null}`),
) )
} else { } else {
_, _ = w.Write([]byte(`{"number":456,"building":false,"duration":3000,"result":"FAILURE"}`)) _, _ = w.Write(
[]byte(
`{"number":456,"building":false,"duration":3000,"result":"FAILURE"}`,
),
)
} }
} }
})) }))
defer server.Close() defer server.Close()
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false) jenkins, err := NewJenkins(context.Background(), auth, server.URL, "", false, "", false)
assert.NoError(t, err) assert.NoError(t, err)
buildInfo, err := jenkins.waitForCompletion( buildInfo, err := jenkins.waitForCompletion(
context.Background(), context.Background(),
"test-job", testJobName,
queueID, queueID,
50*time.Millisecond, 50*time.Millisecond,
5*time.Second, 5*time.Second,
@@ -618,8 +626,8 @@ func TestLoadCACert(t *testing.T) {
func TestNewJenkinsWithCACert(t *testing.T) { func TestNewJenkinsWithCACert(t *testing.T) {
t.Run("with valid CA certificate", func(t *testing.T) { t.Run("with valid CA certificate", func(t *testing.T) {
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
context.Background(), context.Background(),
@@ -642,8 +650,8 @@ func TestNewJenkinsWithCACert(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
context.Background(), context.Background(),
@@ -660,8 +668,8 @@ func TestNewJenkinsWithCACert(t *testing.T) {
t.Run("with invalid CA certificate content", func(t *testing.T) { t.Run("with invalid CA certificate content", func(t *testing.T) {
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
context.Background(), context.Background(),
@@ -679,8 +687,8 @@ func TestNewJenkinsWithCACert(t *testing.T) {
t.Run("with invalid PEM format", func(t *testing.T) { t.Run("with invalid PEM format", func(t *testing.T) {
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
invalidPEM := "-----BEGIN CERTIFICATE-----\ninvalid-base64-data\n-----END CERTIFICATE-----" invalidPEM := "-----BEGIN CERTIFICATE-----\ninvalid-base64-data\n-----END CERTIFICATE-----"
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
@@ -699,8 +707,8 @@ func TestNewJenkinsWithCACert(t *testing.T) {
t.Run("with nonexistent file path", func(t *testing.T) { t.Run("with nonexistent file path", func(t *testing.T) {
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
context.Background(), context.Background(),
@@ -718,8 +726,8 @@ func TestNewJenkinsWithCACert(t *testing.T) {
t.Run("insecure flag takes precedence over CA cert", func(t *testing.T) { t.Run("insecure flag takes precedence over CA cert", func(t *testing.T) {
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
// When insecure is true, CA cert should be ignored // When insecure is true, CA cert should be ignored
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
@@ -737,8 +745,8 @@ func TestNewJenkinsWithCACert(t *testing.T) {
t.Run("without CA certificate uses default client", func(t *testing.T) { t.Run("without CA certificate uses default client", func(t *testing.T) {
auth := &Auth{ auth := &Auth{
Username: "test", Username: testUserName,
Token: "test", Token: testUserName,
} }
jenkins, err := NewJenkins( jenkins, err := NewJenkins(
context.Background(), context.Background(),
+3 -3
View File
@@ -71,7 +71,7 @@ func main() {
EnvVars: []string{"PLUGIN_USER", "JENKINS_USER", "INPUT_USER"}, EnvVars: []string{"PLUGIN_USER", "JENKINS_USER", "INPUT_USER"},
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "token", Name: tokenParam,
Aliases: []string{"t"}, Aliases: []string{"t"},
Usage: "jenkins API token for authentication", Usage: "jenkins API token for authentication",
EnvVars: []string{"PLUGIN_TOKEN", "JENKINS_TOKEN", "INPUT_TOKEN"}, EnvVars: []string{"PLUGIN_TOKEN", "JENKINS_TOKEN", "INPUT_TOKEN"},
@@ -175,7 +175,7 @@ func run(c *cli.Context) error {
} }
// Validate authentication: either (user + token) or remote-token must be provided // Validate authentication: either (user + token) or remote-token must be provided
hasUserAuth := c.String("user") != "" && c.String("token") != "" hasUserAuth := c.String("user") != "" && c.String(tokenParam) != ""
hasRemoteToken := c.String("remote-token") != "" hasRemoteToken := c.String("remote-token") != ""
if !hasUserAuth && !hasRemoteToken { if !hasUserAuth && !hasRemoteToken {
@@ -185,7 +185,7 @@ func run(c *cli.Context) error {
plugin := Plugin{ plugin := Plugin{
BaseURL: c.String("host"), BaseURL: c.String("host"),
Username: c.String("user"), Username: c.String("user"),
Token: c.String("token"), Token: c.String(tokenParam),
RemoteToken: c.String("remote-token"), RemoteToken: c.String("remote-token"),
Job: c.StringSlice("job"), Job: c.StringSlice("job"),
Insecure: c.Bool("insecure"), Insecure: c.Bool("insecure"),
+61 -61
View File
@@ -34,53 +34,53 @@ func TestValidateConfig(t *testing.T) {
{ {
name: "missing authentication", name: "missing authentication",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
}, },
wantError: true, wantError: true,
errorMsg: "authentication required", errorMsg: testAuthRequiredErr,
}, },
{ {
name: "missing token (only username)", name: "missing token (only username)",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
Username: "foo", Username: testUserFoo,
}, },
wantError: true, wantError: true,
errorMsg: "authentication required", errorMsg: testAuthRequiredErr,
}, },
{ {
name: "missing username (only token)", name: "missing username (only token)",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
Token: "bar", Token: testUserBar,
}, },
wantError: true, wantError: true,
errorMsg: "authentication required", errorMsg: testAuthRequiredErr,
}, },
{ {
name: "user and token auth", name: "user and token auth",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
}, },
wantError: false, wantError: false,
}, },
{ {
name: "remote token auth", name: "remote token auth",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
RemoteToken: "remote-token-123", RemoteToken: testRemoteTokenValue,
}, },
wantError: false, wantError: false,
}, },
{ {
name: "both auth methods", name: "both auth methods",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
RemoteToken: "remote-token-123", RemoteToken: testRemoteTokenValue,
}, },
wantError: false, wantError: false,
}, },
@@ -118,7 +118,7 @@ func TestTrimWhitespaceFromSlice(t *testing.T) {
}, },
{ {
name: "all whitespace", name: "all whitespace",
input: []string{" ", "\t", "\n"}, input: []string{testWhitespaceVal, "\t", "\n"},
expected: []string{}, expected: []string{},
}, },
{ {
@@ -129,12 +129,12 @@ func TestTrimWhitespaceFromSlice(t *testing.T) {
{ {
name: "trim surrounding whitespace", name: "trim surrounding whitespace",
input: []string{" foo ", " bar ", "baz"}, input: []string{" foo ", " bar ", "baz"},
expected: []string{"foo", "bar", "baz"}, expected: []string{testUserFoo, testUserBar, "baz"},
}, },
{ {
name: "mixed empty and valid", name: "mixed empty and valid",
input: []string{"", "valid", "", "also-valid", ""}, input: []string{"", testValidStr, "", "also-valid", ""},
expected: []string{"valid", "also-valid"}, expected: []string{testValidStr, "also-valid"},
}, },
} }
@@ -157,29 +157,29 @@ func TestParseParameters(t *testing.T) {
name: "valid parameters", name: "valid parameters",
input: "key1=value1\nkey2=value2", input: "key1=value1\nkey2=value2",
expected: url.Values{ expected: url.Values{
"key1": []string{"value1"}, testParamKey1: []string{testParamValue1},
"key2": []string{"value2"}, testParamKey2: []string{testParamValue2},
}, },
}, },
{ {
name: "parameter with multiple equals signs", name: "parameter with multiple equals signs",
input: "key=value=with=equals", input: "key=value=with=equals",
expected: url.Values{ expected: url.Values{
"key": []string{"value=with=equals"}, testParamKey: []string{"value=with=equals"},
}, },
}, },
{ {
name: "parameter with spaces in value", name: "parameter with spaces in value",
input: "key=value with spaces", input: "key=value with spaces",
expected: url.Values{ expected: url.Values{
"key": []string{"value with spaces"}, testParamKey: []string{"value with spaces"},
}, },
}, },
{ {
name: "parameter with empty value", name: "parameter with empty value",
input: "key=", input: "key=",
expected: url.Values{ expected: url.Values{
"key": []string{""}, testParamKey: []string{""},
}, },
}, },
{ {
@@ -196,15 +196,15 @@ func TestParseParameters(t *testing.T) {
name: "mixed valid and invalid", name: "mixed valid and invalid",
input: "valid=yes\ninvalid\nalso=valid", input: "valid=yes\ninvalid\nalso=valid",
expected: url.Values{ expected: url.Values{
"valid": []string{"yes"}, testValidStr: []string{"yes"},
"also": []string{"valid"}, "also": []string{testValidStr},
}, },
}, },
{ {
name: "key with surrounding whitespace", name: "key with surrounding whitespace",
input: " key =value", input: " key =value",
expected: url.Values{ expected: url.Values{
"key": []string{"value"}, testParamKey: []string{"value"},
}, },
}, },
{ {
@@ -216,16 +216,16 @@ func TestParseParameters(t *testing.T) {
name: "multiple empty lines", name: "multiple empty lines",
input: "key1=value1\n\n\nkey2=value2", input: "key1=value1\n\n\nkey2=value2",
expected: url.Values{ expected: url.Values{
"key1": []string{"value1"}, testParamKey1: []string{testParamValue1},
"key2": []string{"value2"}, testParamKey2: []string{testParamValue2},
}, },
}, },
{ {
name: "lines with whitespace only", name: "lines with whitespace only",
input: "key1=value1\n \n\t\nkey2=value2", input: "key1=value1\n \n\t\nkey2=value2",
expected: url.Values{ expected: url.Values{
"key1": []string{"value1"}, testParamKey1: []string{testParamValue1},
"key2": []string{"value2"}, testParamKey2: []string{testParamValue2},
}, },
}, },
} }
@@ -252,28 +252,28 @@ func TestExecMissingConfig(t *testing.T) {
// TestExecMissingJenkinsUsername tests Exec with missing username // TestExecMissingJenkinsUsername tests Exec with missing username
func TestExecMissingJenkinsUsername(t *testing.T) { func TestExecMissingJenkinsUsername(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
} }
err := plugin.Exec(context.Background()) err := plugin.Exec(context.Background())
assert.Error(t, err) assert.Error(t, err)
assert.Contains(t, err.Error(), "configuration error") assert.Contains(t, err.Error(), "configuration error")
assert.Contains(t, err.Error(), "authentication required") assert.Contains(t, err.Error(), testAuthRequiredErr)
} }
// TestExecMissingJenkinsToken tests Exec with missing token // TestExecMissingJenkinsToken tests Exec with missing token
func TestExecMissingJenkinsToken(t *testing.T) { func TestExecMissingJenkinsToken(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
Username: "foo", Username: testUserFoo,
} }
err := plugin.Exec(context.Background()) err := plugin.Exec(context.Background())
assert.Error(t, err) assert.Error(t, err)
assert.Contains(t, err.Error(), "configuration error") assert.Contains(t, err.Error(), "configuration error")
assert.Contains(t, err.Error(), "authentication required") assert.Contains(t, err.Error(), testAuthRequiredErr)
} }
// TestExecMissingJenkinsJob tests Exec with missing or empty job list // TestExecMissingJenkinsJob tests Exec with missing or empty job list
@@ -288,7 +288,7 @@ func TestExecMissingJenkinsJob(t *testing.T) {
}, },
{ {
name: "only whitespace jobs", name: "only whitespace jobs",
jobs: []string{" ", "\t", "\n"}, jobs: []string{testWhitespaceVal, "\t", "\n"},
}, },
{ {
name: "nil jobs", name: "nil jobs",
@@ -299,9 +299,9 @@ func TestExecMissingJenkinsJob(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: "http://example.com", BaseURL: testExampleURL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
Job: tt.jobs, Job: tt.jobs,
} }
@@ -326,8 +326,8 @@ func TestExecTriggerBuild(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: server.URL, BaseURL: server.URL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
Job: []string{"drone-jenkins"}, Job: []string{"drone-jenkins"},
} }
@@ -353,8 +353,8 @@ func TestExecTriggerMultipleJobs(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: server.URL, BaseURL: server.URL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
Job: []string{"job1", "job2", "job3"}, Job: []string{"job1", "job2", "job3"},
} }
@@ -377,8 +377,8 @@ func TestExecWithParameters(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: server.URL, BaseURL: server.URL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
Job: []string{"parameterized-job"}, Job: []string{"parameterized-job"},
Parameters: "branch=main\nenvironment=production", Parameters: "branch=main\nenvironment=production",
} }
@@ -403,16 +403,16 @@ func TestExecWithRemoteToken(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: server.URL, BaseURL: server.URL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
RemoteToken: "remote-token-123", RemoteToken: testRemoteTokenValue,
Job: []string{"secure-job"}, Job: []string{"secure-job"},
} }
err := plugin.Exec(context.Background()) err := plugin.Exec(context.Background())
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "remote-token-123", receivedToken) assert.Equal(t, testRemoteTokenValue, receivedToken)
} }
// TestExecWithJobsContainingWhitespace tests job list with whitespace // TestExecWithJobsContainingWhitespace tests job list with whitespace
@@ -432,9 +432,9 @@ func TestExecWithJobsContainingWhitespace(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: server.URL, BaseURL: server.URL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
Job: []string{" job1 ", "job2", " ", "job3"}, Job: []string{" job1 ", "job2", testWhitespaceVal, "job3"},
} }
err := plugin.Exec(context.Background()) err := plugin.Exec(context.Background())
@@ -467,9 +467,9 @@ func TestExecWithWaitSuccess(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: server.URL, BaseURL: server.URL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
Job: []string{"test-job"}, Job: []string{testJobName},
Wait: true, Wait: true,
} }
@@ -501,9 +501,9 @@ func TestExecWithWaitFailure(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
BaseURL: server.URL, BaseURL: server.URL,
Username: "foo", Username: testUserFoo,
Token: "bar", Token: testUserBar,
Job: []string{"test-job"}, Job: []string{testJobName},
Wait: true, Wait: true,
} }
+18
View File
@@ -0,0 +1,18 @@
package main
const (
testUserFoo = "foo"
testUserBar = "bar"
testUserName = "test"
testJobName = "test-job"
testExampleURL = "http://example.com"
testAuthRequiredErr = "authentication required"
testRemoteTokenValue = "remote-token-123"
testWhitespaceVal = " "
testValidStr = "valid"
testParamKey = "key"
testParamKey1 = "key1"
testParamKey2 = "key2"
testParamValue1 = "value1"
testParamValue2 = "value2"
)