Merge branch 'main' into main

Signed-off-by: yeomin4242 <111717431+yeomin4242@users.noreply.github.com>
pull/32155/head
yeomin4242 2 months ago committed by GitHub
commit 1de11e668c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -37,9 +37,17 @@ updates:
directory: "/"
schedule:
interval: "daily"
groups:
github-actions:
patterns:
- "*"
- # Keep dev-v3 GitHub Actions up to date, while Helm v3 is within support
package-ecosystem: "github-actions"
target-branch: "dev-v3"
directory: "/"
schedule:
interval: "daily"
groups:
github-actions:
patterns:
- "*"

@ -0,0 +1,2 @@
"v4.x":
- base-branch: "main"

@ -18,11 +18,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v7.0.1
- name: Add variables to environment file
run: cat ".github/env" >> "$GITHUB_ENV"
- name: Setup Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # pin@6.5.0
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # pin@7.0.0
with:
go-version: '${{ env.GOLANG_VERSION }}'
check-latest: true

@ -44,11 +44,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v7.0.1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # pinv4.37.0
uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # pinv4.37.3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -59,7 +59,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # pinv4.37.0
uses: github/codeql-action/autobuild@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # pinv4.37.3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -73,4 +73,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # pinv4.37.0
uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # pinv4.37.3

@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v7.0.1
- name: Add variables to environment file
run: cat ".github/env" >> "$GITHUB_ENV"
- name: Setup Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # pin@6.5.0
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # pin@7.0.0
with:
go-version: '${{ env.GOLANG_VERSION }}'
check-latest: true

@ -19,13 +19,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v7.0.1
with:
persist-credentials: false
- name: Add variables to environment file
run: cat ".github/env" >> "$GITHUB_ENV"
- name: Setup Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # pin@6.5.0
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # pin@7.0.0
with:
go-version: '${{ env.GOLANG_VERSION }}'
check-latest: true

@ -0,0 +1,16 @@
name: Label PRs
on:
pull_request_target:
branches: [main]
types: [opened, reopened, edited, synchronize]
permissions: {}
jobs:
label:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # pin@v7.0.0
with:
sync-labels: true

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest-16-cores
steps:
- name: Checkout source code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v7.0.1
with:
fetch-depth: 0
@ -28,7 +28,7 @@ jobs:
run: cat ".github/env" >> "$GITHUB_ENV"
- name: Setup Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # pin@6.5.0
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # pin@7.0.0
with:
go-version: '${{ env.GOLANG_VERSION }}'
check-latest: true
@ -85,7 +85,7 @@ jobs:
if: github.ref == 'refs/heads/main' && github.repository == 'helm/helm'
steps:
- name: Checkout source code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v7.0.1
with:
fetch-depth: 0
@ -93,7 +93,7 @@ jobs:
run: cat ".github/env" >> "$GITHUB_ENV"
- name: Setup Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # pin@6.5.0
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # pin@7.0.0
with:
go-version: '${{ env.GOLANG_VERSION }}'
check-latest: true

@ -28,12 +28,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
@ -64,6 +64,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
sarif_file: results.sarif

@ -74,9 +74,9 @@ linters:
errorlint:
# Check for plain type assertions and type switches.
asserts: false
asserts: true
# Check for plain error comparisons.
comparison: false
comparison: true
exhaustive:
default-signifies-exhaustive: true

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package main // import "helm.sh/helm/v4/cmd/helm"
package main
import (
"errors"

@ -39,14 +39,14 @@ require (
golang.org/x/term v0.45.0
golang.org/x/text v0.40.0
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.36.2
k8s.io/apiextensions-apiserver v0.36.2
k8s.io/apimachinery v0.36.2
k8s.io/apiserver v0.36.2
k8s.io/cli-runtime v0.36.2
k8s.io/client-go v0.36.2
k8s.io/api v0.36.3
k8s.io/apiextensions-apiserver v0.36.3
k8s.io/apimachinery v0.36.3
k8s.io/apiserver v0.36.3
k8s.io/cli-runtime v0.36.3
k8s.io/client-go v0.36.3
k8s.io/klog/v2 v2.140.0
k8s.io/kubectl v0.36.2
k8s.io/kubectl v0.36.3
oras.land/oras-go/v2 v2.6.2
sigs.k8s.io/controller-runtime v0.24.1
sigs.k8s.io/kustomize/kyaml v0.21.1
@ -165,18 +165,18 @@ require (
golang.org/x/sys v0.47.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.47.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/grpc v1.82.1 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/component-base v0.36.2 // indirect
k8s.io/component-base v0.36.3 // indirect
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/kustomize/api v0.21.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.3 // indirect
)

@ -465,12 +465,12 @@ golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@ -487,26 +487,26 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.36.2 h1:TF6YDLIzKfccK7cq9YpTcGX8TJmEkHVRv78DM51fRYY=
k8s.io/api v0.36.2/go.mod h1:F4LbMO4brjZYh7yFkXWhynSvtB7YauxV4c+HHkNRGNg=
k8s.io/apiextensions-apiserver v0.36.2 h1:3O5gqOj/dt2XWWbpMe+TXWpE9yU6pjM/tXxtHHJT/K4=
k8s.io/apiextensions-apiserver v0.36.2/go.mod h1:cL1tBWe8XSaP1H30iWKGo7hf6iAUUUJPEU70dskmAnA=
k8s.io/apimachinery v0.36.2 h1:0PE/W/WNy1UX61NLbXY5TMbJ6UwLL6E6lAPkYrKFxbQ=
k8s.io/apimachinery v0.36.2/go.mod h1:fvf/HOLXq9RId0rnDIbN1OEBvHXdQbLMM8nu0LcBUf4=
k8s.io/apiserver v0.36.2 h1:6vMnkmHZPeBloNkHUhmZYq7Ylv8WIB8xjyEl+eSt26E=
k8s.io/apiserver v0.36.2/go.mod h1:9PoQ2ikCytrZyZg11mGhLEF5m8Rgsb5FJmYJ4Wvnl1k=
k8s.io/cli-runtime v0.36.2 h1:CconTvEeV4DJs4ZX3HQKCFbFRGsm6OtuBM9yjmMP2VM=
k8s.io/cli-runtime v0.36.2/go.mod h1:LddcjiMf4YlnHO7c1Y7rEtDqL84FyiYVLco7V679GUU=
k8s.io/client-go v0.36.2 h1:bfgxmFKc9CgqsgX4xKLAAdmTQlWee7Ob/HlDOrJ5TBI=
k8s.io/client-go v0.36.2/go.mod h1:1vgO4OAlfPnoLcb+Rze2GF5rAr14w8qjrYMoyXJzQj0=
k8s.io/component-base v0.36.2 h1:Z0VH80O7Ng0HDZnZj3WRR3urEGa0kTwmO8CwEwjVK1w=
k8s.io/component-base v0.36.2/go.mod h1:mGfFOA7Gwpdm1VW2cwSQYbiDIlz8GD2WGwH88QSeCyA=
k8s.io/api v0.36.3 h1:NxB+05W2UGqXWFXcLO0RB5cnqnUPP5v5sVlaOH0Iz4w=
k8s.io/api v0.36.3/go.mod h1:JzLQKqRHC5+I8RVj/lS3lCg0mg6nWI9Fo/Sk3ElxHzg=
k8s.io/apiextensions-apiserver v0.36.3 h1:dPmOAPhwTtqb1bTxbFPsy18KHPhktQeO3WUPXunZIB0=
k8s.io/apiextensions-apiserver v0.36.3/go.mod h1:KTXFqgXiuw2pRoL+Wpmttqc+up9Xt/GohadPWeLLOa4=
k8s.io/apimachinery v0.36.3 h1:PkzMRBRG8joFD8EhCuQAtNPvJlxb82FwplP26HIzvAM=
k8s.io/apimachinery v0.36.3/go.mod h1:cTSjBWgPe/6CQyBKzY/hDIRWCQQQeK0mfLbml0UYFHE=
k8s.io/apiserver v0.36.3 h1:MGSg2SkdfuytiDEcRylT5mQFmmSsbx90XFUO67Y4bsQ=
k8s.io/apiserver v0.36.3/go.mod h1:fVH7zv9EUNUA7Fl7LtDKh8aB9W7u1VQPSGtWV5SjUxg=
k8s.io/cli-runtime v0.36.3 h1:g+eJ+M1sYpnNYp/q5fzaw2KejIL0Q7DH+xFl6YVoL4U=
k8s.io/cli-runtime v0.36.3/go.mod h1:hZpAqK8nSFXvvLaVCbzUPVp8e9TRLSTCfpNzMt7s3tE=
k8s.io/client-go v0.36.3 h1:M4JdVzXxYcZk4fGpfDdYnxSwhLKWCFoQsHW6t+z8Hfg=
k8s.io/client-go v0.36.3/go.mod h1:gcPwr0c87vjjG6HB6pWEqOeuYVoXSsREjzux2j6GF30=
k8s.io/component-base v0.36.3 h1:vc/UFvPCkW0irPz84LAodAL1j3f4xktPM6dDJIEheAY=
k8s.io/component-base v0.36.3/go.mod h1:hZbNFG+gCMl9EbykDGEu73feKP9/Cq6JsV4pTo9GTO8=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
k8s.io/kubectl v0.36.2 h1:rpUGGpeL09XVOLep2yle5jrtk//JA1L6ZHfkQQtVEwk=
k8s.io/kubectl v0.36.2/go.mod h1:gVbQ3B/yb4bSR2ggQ7rd0W6icUSWs7sduH4e16Vii+0=
k8s.io/kubectl v0.36.3 h1:TesKp+XYQEjPYoFvuobcVnuvira2+/xAVlq//+kksaI=
k8s.io/kubectl v0.36.3/go.mod h1:W+NEb1CzBGmoaI1Nrpn2ETo9omNBl0AsyxnnMT40N6E=
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 h1:wU4tMEhLGgIbLvXQb1cfN+EcM0wf7zC6CPF+C79jroc=
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
oras.land/oras-go/v2 v2.6.2 h1:N04RXngAp1LJKTG6ifz3xHPipasEkWr+hFmInja5YKo=
@ -521,7 +521,7 @@ sigs.k8s.io/kustomize/kyaml v0.21.1 h1:IVlbmhC076nf6foyL6Taw4BkrLuEsXUXNpsE+ScX7
sigs.k8s.io/kustomize/kyaml v0.21.1/go.mod h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/structured-merge-diff/v6 v6.3.3 h1:u08YRbVUi59ri4YD6cg0UqNM4Dimn0sIl+wldcx5PYw=
sigs.k8s.io/structured-merge-diff/v6 v6.3.3/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

@ -17,6 +17,8 @@ package v3
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestValidateDependency(t *testing.T) {
@ -35,10 +37,10 @@ func TestValidateDependency(t *testing.T) {
} {
dep.Alias = value
res := dep.Validate()
if res != nil && !shouldFail {
t.Errorf("Failed on case %q", dep.Alias)
} else if res == nil && shouldFail {
t.Errorf("Expected failure for %q", dep.Alias)
if shouldFail {
assert.Errorf(t, res, "Expected failure for %q", dep.Alias)
} else {
assert.NoErrorf(t, res, "Failed on case %q", dep.Alias)
}
}
}

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package lint // import "helm.sh/helm/v4/internal/chart/v3/lint"
package lint
import (
"path/filepath"

@ -114,7 +114,7 @@ func TestBadValuesV3(t *testing.T) {
func TestBadCrdFileV3(t *testing.T) {
var values map[string]any
m := RunAll(badCrdFileDir, values, namespace).Messages
assert.Lenf(t, m, 2, "All didn't fail with expected errors, got %#v", m)
require.Lenf(t, m, 2, "All didn't fail with expected errors, got %#v", m)
require.ErrorContains(t, m[0].Err, "apiVersion is not in 'apiextensions.k8s.io'")
assert.ErrorContains(t, m[1].Err, "object kind is not 'CustomResourceDefinition'")
}
@ -122,8 +122,7 @@ func TestBadCrdFileV3(t *testing.T) {
func TestGoodChart(t *testing.T) {
var values map[string]any
m := RunAll(goodChartDir, values, namespace).Messages
if len(m) != 0 {
t.Error("All returned linter messages when it shouldn't have")
if !assert.Empty(t, m, "All returned linter messages when it shouldn't have") {
for i, msg := range m {
t.Logf("Message %d: %s", i, msg)
}
@ -143,13 +142,12 @@ func TestHelmCreateChart(t *testing.T) {
// Note: we test with strict=true here, even though others have
// strict = false.
m := RunAll(createdChart, values, namespace, WithSkipSchemaValidation(true)).Messages
if ll := len(m); ll != 1 {
t.Errorf("All should have had exactly 1 error. Got %d", ll)
if !assert.Len(t, m, 1, "All should have had exactly 1 error") {
for i, msg := range m {
t.Logf("Message %d: %s", i, msg.Error())
}
} else if msg := m[0].Err.Error(); !strings.Contains(msg, "icon is recommended") {
t.Errorf("Unexpected lint error: %s", msg)
} else {
assert.ErrorContains(t, m[0].Err, "icon is recommended")
}
}
@ -192,8 +190,7 @@ func TestHelmCreateChart_CheckDeprecatedWarnings(t *testing.T) {
func TestSubChartValuesChart(t *testing.T) {
var values map[string]any
m := RunAll(subChartValuesDir, values, namespace).Messages
if len(m) != 0 {
t.Error("All returned linter messages when it shouldn't have")
if !assert.Empty(t, m, "All returned linter messages when it shouldn't have") {
for i, msg := range m {
t.Logf("Message %d: %s", i, msg)
}

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package rules // import "helm.sh/helm/v4/internal/chart/v3/lint/rules"
package rules
import (
"errors"

@ -52,24 +52,19 @@ func TestValidateChartYamlNotDirectory(t *testing.T) {
_ = os.Mkdir(nonExistingChartFilePath, os.ModePerm)
defer os.Remove(nonExistingChartFilePath)
err := validateChartYamlNotDirectory(nonExistingChartFilePath)
assert.Error(t, err, "validateChartYamlNotDirectory to return a linter error, got no error")
assert.Error(t, validateChartYamlNotDirectory(nonExistingChartFilePath), "validateChartYamlNotDirectory to return a linter error, got no error")
}
func TestValidateChartYamlFormat(t *testing.T) {
err := validateChartYamlFormat(errors.New("Read error"))
require.Error(t, err, "validateChartYamlFormat to return a linter error, got no error")
require.Error(t, validateChartYamlFormat(errors.New("Read error")), "validateChartYamlFormat to return a linter error, got no error")
err = validateChartYamlFormat(nil)
assert.NoError(t, err, "validateChartYamlFormat to return no error, got a linter error")
assert.NoError(t, validateChartYamlFormat(nil), "validateChartYamlFormat to return no error, got a linter error")
}
func TestValidateChartName(t *testing.T) {
err := validateChartName(badChart)
require.Error(t, err, "validateChartName to return a linter error, got no error")
require.Error(t, validateChartName(badChart), "validateChartName to return a linter error, got no error")
err = validateChartName(badChartName)
assert.Error(t, err, "expected validateChartName to return a linter error for an invalid name, got no error")
assert.Error(t, validateChartName(badChartName), "expected validateChartName to return a linter error for an invalid name, got no error")
}
func TestValidateChartVersion(t *testing.T) {
@ -90,15 +85,13 @@ func TestValidateChartVersion(t *testing.T) {
for i, test := range failTest {
t.Run(strconv.Itoa(i), func(t *testing.T) {
badChart.Version = test.Version
err := validateChartVersion(badChart)
require.ErrorContains(t, err, test.ErrorMsg, "validateChartVersion(%s) to return \"%s\", got no error", test.Version, test.ErrorMsg)
require.ErrorContains(t, validateChartVersion(badChart), test.ErrorMsg, "validateChartVersion(%s) to return \"%s\", got no error", test.Version, test.ErrorMsg)
})
}
for _, version := range successTest {
badChart.Version = version
err := validateChartVersion(badChart)
assert.NoError(t, err, "validateChartVersion(%s) to return no error, got a linter error", version)
assert.NoError(t, validateChartVersion(badChart), "validateChartVersion(%s) to return no error, got a linter error", version)
}
}
@ -124,24 +117,20 @@ func TestValidateChartMaintainer(t *testing.T) {
for _, test := range failTest {
t.Run(fmt.Sprintf("%s, %s", test.Name, test.Email), func(t *testing.T) {
badChart.Maintainers = []*chart.Maintainer{{Name: test.Name, Email: test.Email}}
err := validateChartMaintainer(badChart)
require.ErrorContains(t, err, test.ErrorMsg, "validateChartMaintainer(%s, %s) to return \"%s\", got no error", test.Name, test.Email, test.ErrorMsg)
require.ErrorContains(t, validateChartMaintainer(badChart), test.ErrorMsg, "validateChartMaintainer(%s, %s) to return \"%s\", got no error", test.Name, test.Email, test.ErrorMsg)
})
}
for _, test := range successTest {
t.Run(fmt.Sprintf("%s, %s", test.Name, test.Email), func(t *testing.T) {
badChart.Maintainers = []*chart.Maintainer{{Name: test.Name, Email: test.Email}}
err := validateChartMaintainer(badChart)
require.NoError(t, err, "validateChartMaintainer(%s, %s) to return no error", test.Name, test.Email)
require.NoError(t, validateChartMaintainer(badChart), "validateChartMaintainer(%s, %s) to return no error", test.Name, test.Email)
})
}
// Testing for an empty maintainer
badChart.Maintainers = []*chart.Maintainer{nil}
err := validateChartMaintainer(badChart)
require.Error(t, err, "validateChartMaintainer did not return error for nil maintainer as expected")
assert.EqualError(t, err, "a maintainer entry is empty", "validateChartMaintainer returned unexpected error for nil maintainer")
assert.EqualError(t, validateChartMaintainer(badChart), "a maintainer entry is empty")
}
func TestValidateChartSources(t *testing.T) {
@ -150,15 +139,13 @@ func TestValidateChartSources(t *testing.T) {
for _, test := range failTest {
t.Run(test, func(t *testing.T) {
badChart.Sources = []string{test}
err := validateChartSources(badChart)
require.ErrorContains(t, err, "invalid source URL", "validateChartSources(%s) to return \"invalid source URL\", got no error", test)
require.ErrorContains(t, validateChartSources(badChart), "invalid source URL", "validateChartSources(%s) to return \"invalid source URL\", got no error", test)
})
}
for _, test := range successTest {
badChart.Sources = []string{test}
err := validateChartSources(badChart)
assert.NoError(t, err, "validateChartSources(%s) to return no error", test)
assert.NoError(t, validateChartSources(badChart), "validateChartSources(%s) to return no error", test)
}
}
@ -168,19 +155,13 @@ func TestValidateChartIconPresence(t *testing.T) {
Icon: "",
}
err := validateChartIconPresence(testChart)
require.Error(t, err, "validateChartIconPresence to return a linter error, got no error")
assert.ErrorContains(t, err, "icon is recommended", "expected %q", "icon is recommended")
assert.ErrorContains(t, validateChartIconPresence(testChart), "icon is recommended", "expected %q", "icon is recommended")
})
t.Run("Icon present", func(t *testing.T) {
testChart := &chart.Metadata{
Icon: "http://example.org/icon.png",
}
err := validateChartIconPresence(testChart)
assert.NoError(t, err, "Unexpected error")
assert.NoError(t, validateChartIconPresence(testChart))
})
}
@ -190,15 +171,13 @@ func TestValidateChartIconURL(t *testing.T) {
for _, test := range failTest {
t.Run(test, func(t *testing.T) {
badChart.Icon = test
err := validateChartIconURL(badChart)
require.ErrorContains(t, err, "invalid icon URL", "validateChartIconURL(%s) to return \"invalid icon URL\", got no error", test)
require.ErrorContains(t, validateChartIconURL(badChart), "invalid icon URL", "validateChartIconURL(%s) to return \"invalid icon URL\", got no error", test)
})
}
for _, test := range successTest {
badChart.Icon = test
err := validateChartIconURL(badChart)
assert.NoError(t, err, "validateChartIconURL(%s) to return no error", test)
assert.NoError(t, validateChartIconURL(badChart), "validateChartIconURL(%s) to return no error", test)
}
}

@ -22,6 +22,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/internal/chart/v3/lint/support"
)
@ -33,7 +34,7 @@ func TestInvalidCrdsDir(t *testing.T) {
Crds(&linter)
res := linter.Messages
assert.Len(t, res, 1)
require.Len(t, res, 1)
assert.ErrorContains(t, res[0].Err, "not a directory")
}

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package rules // import "helm.sh/helm/v4/internal/chart/v3/lint/rules"
package rules
import (
"fmt"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package rules // import "helm.sh/helm/v4/internal/chart/v3/lint/rules"
package rules
import (
"fmt"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package rules // import "helm.sh/helm/v4/internal/chart/v3/lint/rules"
package rules
import (
"testing"
@ -29,14 +29,12 @@ func TestValidateNoDeprecations(t *testing.T) {
Kind: "Deployment",
}
err := validateNoDeprecations(deprecated, nil)
var depErr deprecatedAPIError
require.Error(t, err, "Expected deprecated extension to be flagged")
var depErr deprecatedAPIError
require.ErrorAs(t, err, &depErr, "Expected error to be of type deprecatedAPIError")
require.NotEmpty(t, depErr.Message, "Expected error message to be non-blank: %v", err)
err = validateNoDeprecations(&k8sYamlStruct{
require.NotEmpty(t, depErr.Message, "Expected error message to be non-blank")
assert.NoError(t, validateNoDeprecations(&k8sYamlStruct{
APIVersion: "v1",
Kind: "Pod",
}, nil)
assert.NoError(t, err, "Expected a v1 Pod to not be deprecated")
}, nil), "Expected a v1 Pod to not be deprecated")
}

@ -37,13 +37,11 @@ const templateTestBasedir = "./testdata/albatross"
func TestValidateAllowedExtension(t *testing.T) {
var failTest = []string{"/foo", "/test.toml"}
for _, test := range failTest {
err := validateAllowedExtension(test)
require.ErrorContains(t, err, "Valid extensions are .yaml, .yml, .tpl, or .txt", "validateAllowedExtension('%s') to return \"Valid extensions are .yaml, .yml, .tpl, or .txt\", got no error", test)
require.ErrorContains(t, validateAllowedExtension(test), "Valid extensions are .yaml, .yml, .tpl, or .txt", "validateAllowedExtension('%s') to return \"Valid extensions are .yaml, .yml, .tpl, or .txt\", got no error", test)
}
var successTest = []string{"/foo.yaml", "foo.yaml", "foo.tpl", "/foo/bar/baz.yaml", "NOTES.txt"}
for _, test := range successTest {
err := validateAllowedExtension(test)
assert.NoError(t, err, "validateAllowedExtension('%s') to return no error", test)
assert.NoError(t, validateAllowedExtension(test), "validateAllowedExtension('%s') to return no error", test)
}
}
@ -58,8 +56,7 @@ func TestTemplateParsing(t *testing.T) {
res := linter.Messages
require.Len(t, res, 1, "Expected one error, got %d, %v", len(res), res)
assert.ErrorContains(t, res[0].Err, "deliberateSyntaxError", "Unexpected error: %s", res[0])
assert.ErrorContains(t, res[0].Err, "deliberateSyntaxError")
}
var wrongTemplatePath = filepath.Join(templateTestBasedir, "templates", "fail.yaml")
@ -86,7 +83,7 @@ func TestMultiTemplateFail(t *testing.T) {
require.Len(t, res, 1, "Expected 1 error, got %d, %v", len(res), res)
assert.ErrorContains(t, res[0].Err, "object name does not conform to Kubernetes naming requirements", "Unexpected error: %s", res[0].Err)
assert.ErrorContains(t, res[0].Err, "object name does not conform to Kubernetes naming requirements")
}
func TestValidateMetadataName(t *testing.T) {
@ -202,12 +199,12 @@ func TestDeprecatedAPIFails(t *testing.T) {
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())}
Templates(&linter, values, namespace, strict)
if l := len(linter.Messages); l != 1 {
if !assert.Len(t, linter.Messages, 1) {
for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg)
}
t.Fatalf("Expected 1 lint error, got %d", l)
}
require.Len(t, linter.Messages, 1, "Expected 1 lint error")
var err deprecatedAPIError
require.ErrorAs(t, linter.Messages[0].Err, &err, "Expected error to be of type deprecatedAPIError")
@ -255,8 +252,7 @@ func TestStrictTemplateParsingMapError(t *testing.T) {
ChartDir: filepath.Join(dir, ch.Metadata.Name),
}
Templates(linter, ch.Values, namespace, strict)
if len(linter.Messages) != 0 {
t.Errorf("expected zero messages, got %d", len(linter.Messages))
if !assert.Empty(t, linter.Messages, "expected zero messages") {
for i, msg := range linter.Messages {
t.Logf("Message %d: %q", i, msg)
}
@ -292,8 +288,7 @@ spec:
- name: nginx
image: nginx:1.14.2
`
err := validateMatchSelector(md, manifest)
require.NoError(t, err)
require.NoError(t, validateMatchSelector(md, manifest))
manifest = `
apiVersion: apps/v1
kind: Deployment
@ -315,8 +310,7 @@ spec:
- name: nginx
image: nginx:1.14.2
`
err = validateMatchSelector(md, manifest)
require.NoError(t, err)
require.NoError(t, validateMatchSelector(md, manifest))
manifest = `
apiVersion: apps/v1
kind: Deployment
@ -335,8 +329,7 @@ spec:
- name: nginx
image: nginx:1.14.2
`
err = validateMatchSelector(md, manifest)
assert.Error(t, err, "expected Deployment with no selector to fail")
assert.Error(t, validateMatchSelector(md, manifest), "expected Deployment with no selector to fail")
}
func TestValidateTopIndentLevel(t *testing.T) {
@ -349,8 +342,11 @@ func TestValidateTopIndentLevel(t *testing.T) {
" apiVersion:foo": true,
"\n\n apiVersion:foo\n\n": true,
} {
if err := validateTopIndentLevel(doc); (err == nil) == shouldFail {
t.Errorf("Expected %t for %q", shouldFail, doc)
err := validateTopIndentLevel(doc)
if shouldFail {
assert.Errorf(t, err, "Expected %t for %q", shouldFail, doc)
} else {
assert.NoErrorf(t, err, "Expected %t for %q", shouldFail, doc)
}
}
}
@ -379,12 +375,12 @@ func TestEmptyWithCommentsManifests(t *testing.T) {
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())}
Templates(&linter, values, namespace, strict)
if l := len(linter.Messages); l > 0 {
if !assert.Empty(t, linter.Messages) {
for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg)
}
t.Fatalf("Expected 0 lint errors, got %d", l)
}
require.Empty(t, linter.Messages, "Expected 0 lint errors")
}
func TestValidateListAnnotations(t *testing.T) {
md := &k8sYamlStruct{

@ -56,8 +56,7 @@ func TestValidateValuesYamlNotDirectory(t *testing.T) {
_ = os.Mkdir(nonExistingValuesFilePath, os.ModePerm)
defer os.Remove(nonExistingValuesFilePath)
err := validateValuesFileExistence(nonExistingValuesFilePath)
assert.Error(t, err, "validateValuesFileExistence to return a linter error, got no error")
assert.Error(t, validateValuesFileExistence(nonExistingValuesFilePath), "validateValuesFileExistence to return a linter error, got no error")
}
func TestValidateValuesFileWellFormed(t *testing.T) {
@ -150,7 +149,7 @@ func TestValidateValuesFile(t *testing.T) {
require.NoError(t, err, "Failed validation")
} else {
require.Error(t, err, "expected values file to fail parsing")
assert.ErrorContains(t, err, tt.errorMessage, "Failed with unexpected error")
assert.ErrorContains(t, err, tt.errorMessage)
}
})
}

@ -20,4 +20,4 @@ Package support contains tools for linting charts.
Linting is the process of testing charts for errors or warnings regarding
formatting, compilation, or standards compliance.
*/
package support // import "helm.sh/helm/v4/internal/chart/v3/lint/support"
package support

@ -19,6 +19,9 @@ package support
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var errLint = errors.New("lint failed")
@ -47,33 +50,19 @@ func TestRunLinterRule(t *testing.T) {
linter := Linter{}
for _, test := range tests {
isValid := linter.RunLinterRule(test.Severity, "chart", test.LintError)
if len(linter.Messages) != test.ExpectedMessages {
t.Errorf("RunLinterRule(%d, \"chart\", %v), linter.Messages should now have %d message, we got %d", test.Severity, test.LintError, test.ExpectedMessages, len(linter.Messages))
}
if linter.HighestSeverity != test.ExpectedHighestSeverity {
t.Errorf("RunLinterRule(%d, \"chart\", %v), linter.HighestSeverity should be %d, we got %d", test.Severity, test.LintError, test.ExpectedHighestSeverity, linter.HighestSeverity)
}
if isValid != test.ExpectedReturn {
t.Errorf("RunLinterRule(%d, \"chart\", %v), should have returned %t but returned %t", test.Severity, test.LintError, test.ExpectedReturn, isValid)
}
assert.Lenf(t, linter.Messages, test.ExpectedMessages, "RunLinterRule(%d, \"chart\", %v), linter.Messages should now have %d message, we got %d", test.Severity, test.LintError, test.ExpectedMessages, len(linter.Messages))
assert.Equalf(t, test.ExpectedHighestSeverity, linter.HighestSeverity, "RunLinterRule(%d, \"chart\", %v), linter.HighestSeverity should be %d, we got %d", test.Severity, test.LintError, test.ExpectedHighestSeverity, linter.HighestSeverity)
assert.Equalf(t, test.ExpectedReturn, isValid, "RunLinterRule(%d, \"chart\", %v), should have returned %t but returned %t", test.Severity, test.LintError, test.ExpectedReturn, isValid)
}
}
func TestMessage(t *testing.T) {
m := Message{ErrorSev, "Chart.yaml", errors.New("Foo")}
if m.Error() != "[ERROR] Chart.yaml: Foo" {
t.Errorf("Unexpected output: %s", m.Error())
}
require.EqualError(t, m, "[ERROR] Chart.yaml: Foo")
m = Message{WarningSev, "templates/", errors.New("Bar")}
if m.Error() != "[WARNING] templates/: Bar" {
t.Errorf("Unexpected output: %s", m.Error())
}
require.EqualError(t, m, "[WARNING] templates/: Bar")
m = Message{InfoSev, "templates/rc.yaml", errors.New("FooBar")}
if m.Error() != "[INFO] templates/rc.yaml: FooBar" {
t.Errorf("Unexpected output: %s", m.Error())
}
assert.EqualError(t, m, "[INFO] templates/rc.yaml: FooBar")
}

@ -25,7 +25,6 @@ import (
"log"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"testing"
@ -41,13 +40,9 @@ import (
func TestLoadDir(t *testing.T) {
l, err := Loader("testdata/frobnitz")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
c, err := l.Load()
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
verifyFrobnitz(t, c)
verifyChart(t, c)
verifyDependencies(t, c)
@ -60,33 +55,24 @@ func TestLoadDirWithDevNull(t *testing.T) {
}
l, err := Loader("testdata/frobnitz_with_dev_null")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
if _, err := l.Load(); err == nil {
t.Error("packages with an irregular file (/dev/null) should not load")
}
require.NoError(t, err, "Failed to load testdata")
_, err = l.Load()
assert.Error(t, err, "packages with an irregular file (/dev/null) should not load")
}
func TestLoadDirWithSymlink(t *testing.T) {
sym := filepath.Join("..", "LICENSE")
link := filepath.Join("testdata", "frobnitz_with_symlink", "LICENSE")
if err := os.Symlink(sym, link); err != nil {
t.Fatal(err)
}
require.NoError(t, os.Symlink(sym, link))
defer os.Remove(link)
l, err := Loader("testdata/frobnitz_with_symlink")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
c, err := l.Load()
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
verifyFrobnitz(t, c)
verifyChart(t, c)
verifyDependencies(t, c)
@ -109,8 +95,7 @@ func TestBomTestData(t *testing.T) {
defer unzipped.Close()
for _, testFile := range testFiles {
data := make([]byte, 3)
err := unzipped.Reset(bytes.NewReader(archive))
require.NoErrorf(t, err, "Error reading archive frobnitz_with_bom.tgz")
require.NoErrorf(t, unzipped.Reset(bytes.NewReader(archive)), "Error reading archive frobnitz_with_bom.tgz")
tr := tar.NewReader(unzipped)
for {
file, err := tr.Next()
@ -132,13 +117,9 @@ func TestBomTestData(t *testing.T) {
func TestLoadDirWithUTFBOM(t *testing.T) {
l, err := Loader("testdata/frobnitz_with_bom")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
c, err := l.Load()
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
verifyFrobnitz(t, c)
verifyChart(t, c)
verifyDependencies(t, c)
@ -148,13 +129,9 @@ func TestLoadDirWithUTFBOM(t *testing.T) {
func TestLoadArchiveWithUTFBOM(t *testing.T) {
l, err := Loader("testdata/frobnitz_with_bom.tgz")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
c, err := l.Load()
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
verifyFrobnitz(t, c)
verifyChart(t, c)
verifyDependencies(t, c)
@ -164,13 +141,9 @@ func TestLoadArchiveWithUTFBOM(t *testing.T) {
func TestLoadFile(t *testing.T) {
l, err := Loader("testdata/frobnitz-1.2.3.tgz")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
c, err := l.Load()
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
verifyFrobnitz(t, c)
verifyChart(t, c)
verifyDependencies(t, c)
@ -224,36 +197,16 @@ icon: https://example.com/64x64.png
}
c, err := LoadFiles(goodFiles)
if err != nil {
t.Errorf("Expected good files to be loaded, got %v", err)
}
if c.Name() != "frobnitz" {
t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Name())
}
if c.Values["var"] != "some values" {
t.Error("Expected chart values to be populated with default values")
}
if len(c.Raw) != 5 {
t.Errorf("Expected %d files, got %d", 5, len(c.Raw))
}
if !bytes.Equal(c.Schema, []byte("type: Values")) {
t.Error("Expected chart schema to be populated with default values")
}
if len(c.Templates) != 2 {
t.Errorf("Expected number of templates == 2, got %d", len(c.Templates))
}
if _, err = LoadFiles([]*archive.BufferedFile{}); err == nil {
t.Fatal("Expected err to be non-nil")
}
if err.Error() != "Chart.yaml file is missing" {
t.Errorf("Expected chart metadata missing error, got '%s'", err.Error())
}
require.NoError(t, err, "Expected good files to be loaded")
assert.Equal(t, "frobnitz", c.Name(), "Expected chart name to be 'frobnitz', got %s", c.Name())
assert.Equal(t, "some values", c.Values["var"], "Expected chart values to be populated with default values")
assert.Len(t, c.Raw, 5, "Expected 5 files")
assert.True(t, bytes.Equal(c.Schema, []byte("type: Values")), "Expected chart schema to be populated with default values")
assert.Len(t, c.Templates, 2, "Expected 2 templates")
_, err = LoadFiles([]*archive.BufferedFile{})
require.Error(t, err, "Expected err to be non-nil")
assert.EqualError(t, err, "Chart.yaml file is missing", "Expected chart metadata missing error, got '%s'", err.Error())
}
// Test the order of file loading. The Chart.yaml file needs to come first for
@ -309,9 +262,7 @@ icon: https://example.com/64x64.png
// Capture stderr to make sure message about Chart.yaml handle dependencies
// is not present
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("Unable to create pipe: %s", err)
}
require.NoError(t, err, "Unable to create pipe")
stderr := log.Writer()
log.SetOutput(w)
defer func() {
@ -319,25 +270,19 @@ icon: https://example.com/64x64.png
}()
_, err = LoadFiles(goodFiles)
if err != nil {
t.Errorf("Expected good files to be loaded, got %v", err)
}
require.NoError(t, err, "Expected good files to be loaded")
w.Close()
var text bytes.Buffer
io.Copy(&text, r)
if text.String() != "" {
t.Errorf("Expected no message to Stderr, got %s", text.String())
}
assert.Empty(t, text.String(), "Expected no message to Stderr, got %s", text.String())
}
// Packaging the chart on a Windows machine will produce an
// archive that has \\ as delimiters. Test that we support these archives
func TestLoadFileBackslash(t *testing.T) {
c, err := Load("testdata/frobnitz_backslash-1.2.3.tgz")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
verifyChartFileAndTemplate(t, c, "frobnitz_backslash")
verifyChart(t, c)
verifyDependencies(t, c)
@ -345,13 +290,9 @@ func TestLoadFileBackslash(t *testing.T) {
func TestLoadV3WithReqs(t *testing.T) {
l, err := Loader("testdata/frobnitz.v3.reqs")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
c, err := l.Load()
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
require.NoError(t, err, "Failed to load testdata")
verifyDependencies(t, c)
verifyDependenciesLock(t, c)
}
@ -361,9 +302,7 @@ func TestLoadInvalidArchive(t *testing.T) {
writeTar := func(filename, internalPath string, body []byte) {
dest, err := os.Create(filename)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
zipper := gzip.NewWriter(dest)
tw := tar.NewWriter(zipper)
@ -373,12 +312,9 @@ func TestLoadInvalidArchive(t *testing.T) {
Size: int64(len(body)),
ModTime: time.Now(),
}
if err := tw.WriteHeader(h); err != nil {
t.Fatal(err)
}
if _, err := tw.Write(body); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.WriteHeader(h))
_, err = tw.Write(body)
require.NoError(t, err)
tw.Close()
zipper.Close()
dest.Close()
@ -410,37 +346,27 @@ func TestLoadInvalidArchive(t *testing.T) {
illegalChart := filepath.Join(tmpdir, tt.chartname)
writeTar(illegalChart, tt.internal, []byte("hello: world"))
_, err := Load(illegalChart)
if err == nil {
t.Fatal("expected error when unpacking illegal files")
}
if !strings.Contains(err.Error(), tt.expectError) {
t.Errorf("Expected error to contain %q, got %q for %s", tt.expectError, err.Error(), tt.chartname)
}
require.Error(t, err, "expected error when unpacking illegal files")
require.ErrorContains(t, err, tt.expectError, "Expected error to contain %q, got %q for %s", tt.expectError, err.Error(), tt.chartname)
}
// Make sure that absolute path gets interpreted as relative
illegalChart := filepath.Join(tmpdir, "abs-path.tgz")
writeTar(illegalChart, "/Chart.yaml", []byte("hello: world"))
_, err := Load(illegalChart)
if err.Error() != "validation: chart.metadata.name is required" {
t.Error(err)
}
require.EqualError(t, err, "validation: chart.metadata.name is required")
// And just to validate that the above was not spurious
illegalChart = filepath.Join(tmpdir, "abs-path2.tgz")
writeTar(illegalChart, "files/whatever.yaml", []byte("hello: world"))
_, err = Load(illegalChart)
if err.Error() != "Chart.yaml file is missing" {
t.Errorf("Unexpected error message: %s", err)
}
require.EqualError(t, err, "Chart.yaml file is missing")
// Finally, test that drive letter gets stripped off on Windows
illegalChart = filepath.Join(tmpdir, "abs-winpath.tgz")
writeTar(illegalChart, "c:\\Chart.yaml", []byte("hello: world"))
_, err = Load(illegalChart)
if err.Error() != "validation: chart.metadata.name is required" {
t.Error(err)
}
assert.EqualError(t, err, "validation: chart.metadata.name is required")
}
func TestLoadValues(t *testing.T) {
@ -487,12 +413,8 @@ foo:
for testName, testCase := range testCases {
t.Run(testName, func(tt *testing.T) {
values, err := LoadValues(bytes.NewReader(testCase.data))
if err != nil {
tt.Fatal(err)
}
if !reflect.DeepEqual(values, testCase.expctedValues) {
tt.Errorf("Expected values: %v, got %v", testCase.expctedValues, values)
}
require.NoError(tt, err)
assert.Equalf(tt, testCase.expctedValues, values, "Expected values: %v, got %v", testCase.expctedValues, values)
})
}
}
@ -520,22 +442,13 @@ func TestMergeValuesV3(t *testing.T) {
}
testMap := MergeMaps(flatMap, nestedMap)
equal := reflect.DeepEqual(testMap, nestedMap)
if !equal {
t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap)
}
assert.Equal(t, testMap, nestedMap, "Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap)
testMap = MergeMaps(nestedMap, flatMap)
equal = reflect.DeepEqual(testMap, flatMap)
if !equal {
t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap)
}
assert.Equal(t, testMap, flatMap, "Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap)
testMap = MergeMaps(nestedMap, anotherNestedMap)
equal = reflect.DeepEqual(testMap, anotherNestedMap)
if !equal {
t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap)
}
assert.Equal(t, testMap, anotherNestedMap, "Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap)
testMap = MergeMaps(anotherFlatMap, anotherNestedMap)
expectedMap := map[string]any{
@ -546,32 +459,23 @@ func TestMergeValuesV3(t *testing.T) {
"awesome": "stuff",
},
}
equal = reflect.DeepEqual(testMap, expectedMap)
if !equal {
t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap)
}
assert.Equal(t, expectedMap, testMap, "Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap)
}
func verifyChart(t *testing.T, c *chart.Chart) {
t.Helper()
if c.Name() == "" {
t.Fatalf("No chart metadata found on %v", c)
}
require.NotEmpty(t, c.Name(), "No chart metadata found on %v", c)
t.Logf("Verifying chart %s", c.Name())
if len(c.Templates) != 1 {
t.Errorf("Expected 1 template, got %d", len(c.Templates))
}
assert.Len(t, c.Templates, 1, "Expected 1 template")
numfiles := 6
if len(c.Files) != numfiles {
t.Errorf("Expected %d extra files, got %d", numfiles, len(c.Files))
if !assert.Len(t, c.Files, numfiles, "Expected %d extra files", numfiles) {
for _, n := range c.Files {
t.Logf("\t%s", n.Name)
}
}
if len(c.Dependencies()) != 2 {
t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies()), c.Dependencies())
if !assert.Len(t, c.Dependencies(), 2, "Expected 2 dependencies") {
for _, d := range c.Dependencies() {
t.Logf("\tSubchart: %s\n", d.Name())
}
@ -587,62 +491,40 @@ func verifyChart(t *testing.T, c *chart.Chart) {
}
for _, dep := range c.Dependencies() {
if dep.Metadata == nil {
t.Fatalf("expected metadata on dependency: %v", dep)
}
require.NotNil(t, dep.Metadata, "expected metadata on dependency: %v", dep)
exp, ok := expect[dep.Name()]
if !ok {
t.Fatalf("Unknown dependency %s", dep.Name())
}
if exp["version"] != dep.Metadata.Version {
t.Errorf("Expected %s version %s, got %s", dep.Name(), exp["version"], dep.Metadata.Version)
}
require.True(t, ok, "Unknown dependency %s", dep.Name())
assert.Equal(t, exp["version"], dep.Metadata.Version, "Expected %s version %s, got %s", dep.Name(), exp["version"], dep.Metadata.Version)
}
}
func verifyDependencies(t *testing.T, c *chart.Chart) {
t.Helper()
if len(c.Metadata.Dependencies) != 2 {
t.Errorf("Expected 2 dependencies, got %d", len(c.Metadata.Dependencies))
}
assert.Len(t, c.Metadata.Dependencies, 2, "Expected 2 dependencies")
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Metadata.Dependencies[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}
if d.Version != tt.Version {
t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
}
if d.Repository != tt.Repository {
t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
}
assert.Equal(t, tt.Name, d.Name, "Expected dependency named %q, got %q", tt.Name, d.Name)
assert.Equal(t, tt.Version, d.Version, "Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
assert.Equal(t, tt.Repository, d.Repository, "Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
}
}
func verifyDependenciesLock(t *testing.T, c *chart.Chart) {
t.Helper()
if len(c.Metadata.Dependencies) != 2 {
t.Errorf("Expected 2 dependencies, got %d", len(c.Metadata.Dependencies))
}
assert.Len(t, c.Metadata.Dependencies, 2, "Expected 2 dependencies, got %d", len(c.Metadata.Dependencies))
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Metadata.Dependencies[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}
if d.Version != tt.Version {
t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
}
if d.Repository != tt.Repository {
t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
}
assert.Equal(t, tt.Name, d.Name, "Expected dependency named %q, got %q", tt.Name, d.Name)
assert.Equal(t, tt.Version, d.Version, "Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
assert.Equal(t, tt.Repository, d.Repository, "Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
}
}
@ -653,51 +535,25 @@ func verifyFrobnitz(t *testing.T, c *chart.Chart) {
func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) {
t.Helper()
if c.Metadata == nil {
t.Fatal("Metadata is nil")
}
if c.Name() != name {
t.Errorf("Expected %s, got %s", name, c.Name())
}
if len(c.Templates) != 1 {
t.Fatalf("Expected 1 template, got %d", len(c.Templates))
}
if c.Templates[0].Name != "templates/template.tpl" {
t.Errorf("Unexpected template: %s", c.Templates[0].Name)
}
if len(c.Templates[0].Data) == 0 {
t.Error("No template data.")
}
if len(c.Files) != 6 {
t.Fatalf("Expected 6 Files, got %d", len(c.Files))
}
if len(c.Dependencies()) != 2 {
t.Fatalf("Expected 2 Dependency, got %d", len(c.Dependencies()))
}
if len(c.Metadata.Dependencies) != 2 {
t.Fatalf("Expected 2 Dependencies.Dependency, got %d", len(c.Metadata.Dependencies))
}
if len(c.Lock.Dependencies) != 2 {
t.Fatalf("Expected 2 Lock.Dependency, got %d", len(c.Lock.Dependencies))
}
require.NotNil(t, c.Metadata, "Metadata is nil")
assert.Equal(t, name, c.Name(), "Expected %s, got %s", name, c.Name())
require.Len(t, c.Templates, 1, "Expected 1 template, got %d", len(c.Templates))
assert.Equal(t, "templates/template.tpl", c.Templates[0].Name, "Unexpected template: %s", c.Templates[0].Name)
assert.NotEmpty(t, c.Templates[0].Data, "No template data.")
require.Len(t, c.Files, 6, "Expected 6 Files, got %d", len(c.Files))
require.Len(t, c.Dependencies(), 2, "Expected 2 Dependency, got %d", len(c.Dependencies()))
require.Len(t, c.Metadata.Dependencies, 2, "Expected 2 Dependencies.Dependency, got %d", len(c.Metadata.Dependencies))
require.Len(t, c.Lock.Dependencies, 2, "Expected 2 Lock.Dependency, got %d", len(c.Lock.Dependencies))
for _, dep := range c.Dependencies() {
switch dep.Name() {
case "mariner":
case "alpine":
if len(dep.Templates) != 1 {
t.Fatalf("Expected 1 template, got %d", len(dep.Templates))
}
if dep.Templates[0].Name != "templates/alpine-pod.yaml" {
t.Errorf("Unexpected template: %s", dep.Templates[0].Name)
}
require.Len(t, dep.Templates, 1, "Expected 1 template, got %d", len(dep.Templates))
assert.Equal(t, "templates/alpine-pod.yaml", dep.Templates[0].Name, "Unexpected template: %s", dep.Templates[0].Name)
assert.NotEmpty(t, dep.Templates[0].Data, "No template data.")
if len(dep.Files) != 1 {
t.Fatalf("Expected 1 Files, got %d", len(dep.Files))
}
if len(dep.Dependencies()) != 2 {
t.Fatalf("Expected 2 Dependency, got %d", len(dep.Dependencies()))
}
require.Len(t, dep.Files, 1, "Expected 1 Files, got %d", len(dep.Files))
require.Len(t, dep.Dependencies(), 2, "Expected 2 Dependency, got %d", len(dep.Dependencies()))
default:
t.Errorf("Unexpected dependency %s", dep.Name())
}
@ -707,8 +563,6 @@ func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) {
func verifyBomStripped(t *testing.T, files []*common.File) {
t.Helper()
for _, file := range files {
if bytes.HasPrefix(file.Data, utf8bom) {
t.Errorf("Byte Order Mark still present in processed file %s", file.Name)
}
assert.Falsef(t, bytes.HasPrefix(file.Data, utf8bom), "Byte Order Mark still present in processed file %s", file.Name)
}
}

@ -29,48 +29,28 @@ const testfile = "testdata/chartfiletest.yaml"
func TestLoadChartfile(t *testing.T) {
f, err := LoadChartfile(testfile)
if err != nil {
t.Errorf("Failed to open %s: %s", testfile, err)
return
}
require.NoErrorf(t, err, "Failed to open %s", testfile)
verifyChartfile(t, f, "frobnitz")
}
func verifyChartfile(t *testing.T, f *chart.Metadata, name string) {
t.Helper()
require.NotNil(t, f, "Failed verifyChartfile because f is nil")
assert.Equal(t, name, f.Name, "Expected %s, got %s", name, f.Name)
assert.Equal(t, "This is a frobnitz.", f.Description, "Unexpected description %q", f.Description)
assert.Equal(t, "1.2.3", f.Version, "Unexpected version %q", f.Version)
assert.Len(t, f.Maintainers, 2, "Expected 2 maintainers, got %d", len(f.Maintainers))
assert.Equal(t, "The Helm Team", f.Maintainers[0].Name, "Unexpected maintainer name.")
assert.Equal(t, "nobody@example.com", f.Maintainers[1].Email, "Unexpected maintainer email.")
require.Len(t, f.Sources, 1, "Unexpected number of sources")
assert.Equal(t, "https://example.com/foo/bar", f.Sources[0], "Expected https://example.com/foo/bar, got %s", f.Sources)
assert.Equal(t, "http://example.com", f.Home, "Unexpected home.")
assert.Equal(t, "https://example.com/64x64.png", f.Icon, "Unexpected icon: %q", f.Icon)
require.Len(t, f.Keywords, 3, "Unexpected keywords")
require.Len(t, f.Annotations, 2, "Unexpected annotations")
if want, got := "extravalue", f.Annotations["extrakey"]; want != got {
t.Errorf("Want %q, but got %q", want, got)
}
if want, got := "anothervalue", f.Annotations["anotherkey"]; want != got {
t.Errorf("Want %q, but got %q", want, got)
}
assert.Equal(t, "extravalue", f.Annotations["extrakey"])
assert.Equal(t, "anothervalue", f.Annotations["anotherkey"])
kk := []string{"frobnitz", "sprocket", "dodad"}
for i, k := range f.Keywords {
@ -80,13 +60,9 @@ func verifyChartfile(t *testing.T, f *chart.Metadata, name string) {
func TestIsChartDir(t *testing.T) {
validChartDir, err := IsChartDir("testdata/frobnitz")
if !validChartDir {
t.Errorf("unexpected error while reading chart-directory: (%v)", err)
return
}
require.NoError(t, err, "while reading chart-directory")
require.True(t, validChartDir, "expected valid chart directory")
validChartDir, err = IsChartDir("testdata")
if validChartDir || err == nil {
t.Error("expected error but did not get any")
return
}
require.Error(t, err)
require.False(t, validChartDir, "expected invalid chart directory")
}

@ -38,7 +38,7 @@ func TestCreate(t *testing.T) {
dir := filepath.Join(tdir, "foo")
mychart, err := loader.LoadDir(c)
require.NoError(t, err, "Failed to load newly created chart %q: %s", c, err)
require.NoError(t, err, "Failed to load newly created chart %q", c)
assert.Equal(t, "foo", mychart.Name(), "Expected name to be 'foo', got %q", mychart.Name())
@ -55,9 +55,8 @@ func TestCreate(t *testing.T) {
TestConnectionName,
ValuesfileName,
} {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
t.Errorf("Expected %s file: %s", f, err)
}
_, err := os.Stat(filepath.Join(dir, f))
assert.NoErrorf(t, err, "Expected %s file", f)
}
}
@ -71,14 +70,12 @@ func TestCreateFrom(t *testing.T) {
}
srcdir := "./testdata/frobnitz/charts/mariner"
if err := CreateFrom(cf, tdir, srcdir); err != nil {
t.Fatal(err)
}
require.NoError(t, CreateFrom(cf, tdir, srcdir))
dir := filepath.Join(tdir, "foo")
c := filepath.Join(tdir, cf.Name)
mychart, err := loader.LoadDir(c)
require.NoError(t, err, "Failed to load newly created chart %q: %s", c, err)
require.NoError(t, err, "Failed to load newly created chart %q", c)
assert.Equal(t, "foo", mychart.Name(), "Expected name to be 'foo', got %q", mychart.Name())
@ -87,16 +84,13 @@ func TestCreateFrom(t *testing.T) {
ValuesfileName,
filepath.Join(TemplatesDir, "placeholder.tpl"),
} {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
t.Errorf("Expected %s file: %s", f, err)
}
_, err := os.Stat(filepath.Join(dir, f))
require.NoErrorf(t, err, "Expected %s file", f)
// Check each file to make sure <CHARTNAME> has been replaced
b, err := os.ReadFile(filepath.Join(dir, f))
require.NoError(t, err, "Unable to read file %s: %s", f, err)
if bytes.Contains(b, []byte("<CHARTNAME>")) {
t.Errorf("File %s contains <CHARTNAME>", f)
}
require.NoError(t, err, "Unable to read file %s", f)
assert.Falsef(t, bytes.Contains(b, []byte("<CHARTNAME>")), "File %s contains <CHARTNAME>", f)
}
}
@ -106,9 +100,8 @@ func TestCreate_Overwrite(t *testing.T) {
var errlog bytes.Buffer
if _, err := Create("foo", tdir); err != nil {
t.Fatal(err)
}
_, err := Create("foo", tdir)
require.NoError(t, err)
dir := filepath.Join(tdir, "foo")
@ -117,9 +110,8 @@ func TestCreate_Overwrite(t *testing.T) {
// Now re-run the create
Stderr = &errlog
if _, err := Create("foo", tdir); err != nil {
t.Fatal(err)
}
_, err = Create("foo", tdir)
require.NoError(t, err)
data, err := os.ReadFile(tplname)
require.NoError(t, err)
@ -150,8 +142,11 @@ func TestValidateChartName(t *testing.T) {
"abcdefghijklmnopqrstuvwxyz-_." +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.": false,
} {
if err := validateChartName(name); (err != nil) == shouldPass {
t.Errorf("test for %q failed", name)
err := validateChartName(name)
if shouldPass {
assert.NoErrorf(t, err, "Expected chart name %q to pass validation", name)
} else {
assert.Errorf(t, err, "Expected chart name %q to fail validation, but it passed", name)
}
}
}

@ -369,8 +369,7 @@ func TestDependentChartWithSubChartsAbsentInDependency(t *testing.T) {
require.Len(t, c.Dependencies(), 2, "expected 2 dependencies for this chart, but got %d", len(c.Dependencies()))
err := processDependencyEnabled(c, c.Values, "")
require.NoError(t, err, "expected no errors")
require.NoError(t, processDependencyEnabled(c, c.Values, ""), "expected no errors")
require.Len(t, c.Dependencies(), 2, "expected no changes in dependencies")
}
@ -381,8 +380,7 @@ func TestDependentChartWithSubChartsHelmignore(t *testing.T) {
func TestDependentChartsWithSubChartsSymlink(t *testing.T) {
joonix := filepath.Join("testdata", "joonix")
err := os.Symlink(filepath.Join("..", "..", "frobnitz"), filepath.Join(joonix, "charts", "frobnitz"))
require.NoError(t, err, "failed to create symlink")
require.NoError(t, os.Symlink(filepath.Join("..", "..", "frobnitz"), filepath.Join(joonix, "charts", "frobnitz")), "failed to create symlink")
defer os.RemoveAll(filepath.Join(joonix, "charts", "frobnitz"))
c := loadChart(t, joonix)
@ -394,12 +392,8 @@ func TestDependentChartsWithSubchartsAllSpecifiedInDependency(t *testing.T) {
c := loadChart(t, "testdata/dependent-chart-with-all-in-requirements-yaml")
require.Len(t, c.Dependencies(), 2, "expected 2 dependencies for this chart, but got %d", len(c.Dependencies()))
err := processDependencyEnabled(c, c.Values, "")
require.NoError(t, err, "expected no errors")
require.NoError(t, processDependencyEnabled(c, c.Values, ""), "expected no errors")
require.Len(t, c.Dependencies(), 2, "expected no changes in dependencies")
require.Len(t, c.Dependencies(), len(c.Metadata.Dependencies), "expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies()))
}

@ -47,19 +47,19 @@ func makeTestChartArchive(t *testing.T, chartName, sourceDir string) *bytes.Buff
fStat, err := f.Stat()
require.NoError(t, err)
err = tw.WriteHeader(&tar.Header{
require.NoError(t, tw.WriteHeader(&tar.Header{
Name: filepath.Join(chartName, relPath),
Mode: int64(fStat.Mode()),
Size: fStat.Size(),
})
require.NoError(t, err)
}))
data, err := fs.ReadFile(dir, relPath)
require.NoError(t, err)
tw.Write(data)
_, err = tw.Write(data)
require.NoError(t, err)
}
err := fs.WalkDir(dir, ".", func(path string, d os.DirEntry, walkErr error) error {
require.NoError(t, fs.WalkDir(dir, ".", func(path string, d os.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
@ -71,13 +71,10 @@ func makeTestChartArchive(t *testing.T, chartName, sourceDir string) *bytes.Buff
writeFile(path)
return nil
})
require.NoError(t, err)
}))
err = tw.Close()
require.NoError(t, err)
err = gw.Close()
require.NoError(t, err)
require.NoError(t, tw.Close())
require.NoError(t, gw.Close())
return &result
}
@ -87,26 +84,24 @@ func TestExpand(t *testing.T) {
reader, err := os.Open("testdata/frobnitz-1.2.3.tgz")
require.NoError(t, err)
t.Cleanup(func() { assert.NoError(t, reader.Close()) })
if err := Expand(dest, reader); err != nil {
t.Fatal(err)
}
require.NoError(t, Expand(dest, reader))
expectedChartPath := filepath.Join(dest, "frobnitz")
fi, err := os.Stat(expectedChartPath)
require.NoError(t, err)
if !fi.IsDir() {
t.Fatalf("expected a chart directory at %s", expectedChartPath)
}
require.Truef(t, fi.IsDir(), "expected a chart directory at %s", expectedChartPath)
dir, err := os.Open(expectedChartPath)
require.NoError(t, err)
t.Cleanup(func() { assert.NoError(t, dir.Close()) })
fis, err := dir.Readdir(0)
require.NoError(t, err)
expectLen := 11
assert.Len(t, fis, expectLen, "Expected %d files, but got %d", expectLen, len(fis))
assert.Len(t, fis, expectLen, "Expected %d files", expectLen)
for _, fi := range fis {
expect, err := os.Stat(filepath.Join("testdata", "frobnitz", fi.Name()))
@ -137,8 +132,7 @@ func TestExpandError(t *testing.T) {
archive := makeTestChartArchive(t, tt.chartName, tt.chartDir)
dest := t.TempDir()
err := Expand(dest, archive)
assert.ErrorContains(t, err, tt.wantErr)
assert.ErrorContains(t, Expand(dest, archive), tt.wantErr)
})
}
}
@ -146,16 +140,12 @@ func TestExpandError(t *testing.T) {
func TestExpandFile(t *testing.T) {
dest := t.TempDir()
if err := ExpandFile(dest, "testdata/frobnitz-1.2.3.tgz"); err != nil {
t.Fatal(err)
}
require.NoError(t, ExpandFile(dest, "testdata/frobnitz-1.2.3.tgz"))
expectedChartPath := filepath.Join(dest, "frobnitz")
fi, err := os.Stat(expectedChartPath)
require.NoError(t, err)
if !fi.IsDir() {
t.Fatalf("expected a chart directory at %s", expectedChartPath)
}
require.Truef(t, fi.IsDir(), "expected a chart directory at %s", expectedChartPath)
dir, err := os.Open(expectedChartPath)
require.NoError(t, err)
@ -164,7 +154,7 @@ func TestExpandFile(t *testing.T) {
require.NoError(t, err)
expectLen := 11
assert.Len(t, fis, expectLen, "Expected %d files, but got %d", expectLen, len(fis))
assert.Len(t, fis, expectLen, "Expected %d files", expectLen)
for _, fi := range fis {
expect, err := os.Stat(filepath.Join("testdata", "frobnitz", fi.Name()))

@ -62,20 +62,15 @@ func TestSave(t *testing.T) {
chartWithInvalidJSON := withSchema(*c, []byte("{"))
where, err := Save(c, dest)
require.NoError(t, err, "Failed to save: %s", err)
if !strings.HasPrefix(where, dest) {
t.Fatalf("Expected %q to start with %q", where, dest)
}
if !strings.HasSuffix(where, ".tgz") {
t.Fatalf("Expected %q to end with .tgz", where)
}
require.NoError(t, err, "Failed to save")
require.Truef(t, strings.HasPrefix(where, dest), "Expected %q to start with %q", where, dest)
require.Truef(t, strings.HasSuffix(where, ".tgz"), "Expected %q to end with .tgz", where)
c2, err := loader.LoadFile(where)
require.NoError(t, err)
require.Equal(t, c.Name(), c2.Name(), "Expected chart archive to have %q, got %q", c.Name(), c2.Name())
if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" {
t.Fatal("Files data did not match")
}
require.Len(t, c2.Files, 1, "Files data did not match")
require.Equal(t, "scheherazade/shahryar.txt", c2.Files[0].Name, "Files data did not match")
if !bytes.Equal(c.Schema, c2.Schema) {
indentation := 4
@ -83,13 +78,12 @@ func TestSave(t *testing.T) {
formattedActual := Indent(indentation, string(c2.Schema))
t.Fatalf("Schema data did not match.\nExpected:\n%s\nActual:\n%s", formattedExpected, formattedActual)
}
if _, err := Save(&chartWithInvalidJSON, dest); err == nil {
t.Fatal("Invalid JSON was not caught while saving chart")
}
_, err = Save(&chartWithInvalidJSON, dest)
require.Error(t, err, "Invalid JSON was not caught while saving chart")
c.Metadata.APIVersion = chart.APIVersionV3
where, err = Save(c, dest)
require.NoError(t, err, "Failed to save: %s", err)
require.NoError(t, err, "Failed to save")
c2, err = loader.LoadFile(where)
require.NoError(t, err)
require.NotNil(t, c2.Lock, "Expected v3 chart archive to contain a Chart.lock file")
@ -294,16 +288,14 @@ func TestSavePreservesTimestamps(t *testing.T) {
}
where, err := Save(c, tmp)
require.NoError(t, err, "Failed to save: %s", err)
require.NoError(t, err, "Failed to save")
allHeaders, err := retrieveAllHeadersFromTar(where)
require.NoError(t, err, "Failed to parse tar: %v", err)
require.NoError(t, err, "Failed to parse tar")
roundedTime := initialCreateTime.Round(time.Second)
for _, header := range allHeaders {
if !header.ModTime.Equal(roundedTime) {
t.Fatalf("File timestamp not preserved: %v", header.ModTime)
}
require.Truef(t, header.ModTime.Equal(roundedTime), "File timestamp not preserved: %v", header.ModTime)
}
}
@ -377,8 +369,7 @@ func TestSaveDir(t *testing.T) {
pth := filepath.Join(tmp2, "tmpcharts")
require.NoError(t, os.MkdirAll(filepath.Join(pth), 0o755), "Failed to create directory")
err = SaveDir(c, pth)
assert.EqualError(t, err, "\"../ahab\" is not a valid chart name", "Did not get expected error for chart named %q", c.Name())
assert.EqualError(t, SaveDir(c, pth), "\"../ahab\" is not a valid chart name", "Did not get expected error for chart named %q", c.Name())
}
func TestRepeatableSave(t *testing.T) {
@ -439,10 +430,10 @@ func TestRepeatableSave(t *testing.T) {
// create package
dest := path.Join(tmp, "newdir")
where, err := Save(test.chart, dest)
require.NoError(t, err, "Failed to save: %s", err)
require.NoError(t, err, "Failed to save")
// get shasum for package
result, err := sha256Sum(where)
require.NoError(t, err, "Failed to check shasum: %s", err)
require.NoError(t, err, "Failed to check shasum")
// assert that the package SHA is what we wanted.
assert.Equal(t, test.want, result, "FormatName() result = %v, want %v", result, test.want)
})

@ -16,7 +16,11 @@ limitations under the License.
package util
import "testing"
import (
"testing"
"github.com/stretchr/testify/assert"
)
// TestValidateReleaseName is a regression test for ValidateName
//
@ -46,12 +50,11 @@ func TestValidateReleaseName(t *testing.T) {
"a1111111111111111111111111111111111111111111111111111111111z": false,
}
for input, expectPass := range names {
if err := ValidateReleaseName(input); (err == nil) != expectPass {
st := "fail"
if expectPass {
st = "succeed"
}
t.Errorf("Expected %q to %s", input, st)
err := ValidateReleaseName(input)
if expectPass {
assert.NoErrorf(t, err, "Expected release name %q to pass validation", input)
} else {
assert.Errorf(t, err, "Expected release name %q to fail validation, but it passed", input)
}
}
}
@ -80,12 +83,11 @@ func TestValidateMetadataName(t *testing.T) {
"a1111111111111111111111111111111111111111111111111111111111z": false,
}
for input, expectPass := range names {
if err := ValidateMetadataName(input); (err == nil) != expectPass {
st := "fail"
if expectPass {
st = "succeed"
}
t.Errorf("Expected %q to %s", input, st)
err := ValidateMetadataName(input)
if expectPass {
assert.NoError(t, err, "Expected %q to succeed", input)
} else {
assert.Error(t, err, "Expected %q to fail", input)
}
}
}

@ -37,8 +37,7 @@ func TestAtomicWriteFile(t *testing.T) {
reader := bytes.NewReader([]byte(stringContent))
mode := os.FileMode(0o644)
err := AtomicWriteFile(testpath, reader, mode)
require.NoError(t, err)
require.NoError(t, AtomicWriteFile(testpath, reader, mode))
got, err := os.ReadFile(testpath)
require.NoError(t, err)
@ -58,8 +57,7 @@ func TestAtomicWriteFile_CreateTempError(t *testing.T) {
reader := bytes.NewReader([]byte("test content"))
mode := os.FileMode(0o644)
err := AtomicWriteFile(invalidPath, reader, mode)
assert.Error(t, err, "Expected error when CreateTemp fails")
assert.Error(t, AtomicWriteFile(invalidPath, reader, mode), "Expected error when CreateTemp fails")
}
// TestAtomicWriteFile_EmptyContent tests with empty content
@ -70,8 +68,7 @@ func TestAtomicWriteFile_EmptyContent(t *testing.T) {
reader := bytes.NewReader([]byte(""))
mode := os.FileMode(0o644)
err := AtomicWriteFile(testpath, reader, mode)
require.NoError(t, err, "AtomicWriteFile error with empty content")
require.NoError(t, AtomicWriteFile(testpath, reader, mode), "AtomicWriteFile error with empty content")
got, err := os.ReadFile(testpath)
require.NoError(t, err)
@ -89,8 +86,7 @@ func TestAtomicWriteFile_LargeContent(t *testing.T) {
reader := bytes.NewReader([]byte(largeContent))
mode := os.FileMode(0o644)
err := AtomicWriteFile(testpath, reader, mode)
require.NoError(t, err, "AtomicWriteFile error with large content")
require.NoError(t, AtomicWriteFile(testpath, reader, mode), "AtomicWriteFile error with large content")
got, err := os.ReadFile(testpath)
require.NoError(t, err)

@ -197,9 +197,8 @@ func TestDebugCheckHandler_Handle(t *testing.T) {
}
record := slog.NewRecord(time.Now(), slog.LevelInfo, "test message", 0)
err := handler.Handle(t.Context(), record)
require.NoError(t, err)
require.NoError(t, handler.Handle(t.Context(), record))
assert.Contains(t, buf.String(), "test message")
})
@ -214,9 +213,8 @@ func TestDebugCheckHandler_Handle(t *testing.T) {
type testKey string
ctx := context.WithValue(t.Context(), testKey("test"), "value")
record := slog.NewRecord(time.Now(), slog.LevelInfo, "context test", 0)
err := handler.Handle(ctx, record)
require.NoError(t, err)
require.NoError(t, handler.Handle(ctx, record))
assert.Contains(t, buf.String(), "context test")
})
}

@ -14,7 +14,7 @@ limitations under the License.
*/
// Package cache provides a key generator for vcs urls.
package cache // import "helm.sh/helm/v4/internal/plugin/cache"
package cache
import (
"net/url"

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"path/filepath"

@ -11,10 +11,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestPath(t *testing.T) {
@ -38,8 +40,6 @@ func TestPath(t *testing.T) {
t.Setenv("HELM_PLUGINS", tt.helmPluginsDir)
baseIns := newBase(tt.source)
baseInsPath := baseIns.Path()
if baseInsPath != tt.expectPath {
t.Errorf("expected name %s, got %s", tt.expectPath, baseInsPath)
}
assert.Equal(t, tt.expectPath, baseInsPath, "expected name %s, got %s", tt.expectPath, baseInsPath)
}
}

@ -14,4 +14,4 @@ limitations under the License.
*/
// Package installer provides an interface for installing Helm plugins.
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"archive/tar"

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"bytes"

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"archive/tar"
@ -27,10 +27,14 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/internal/test/ensure"
"helm.sh/helm/v4/pkg/getter"
"helm.sh/helm/v4/pkg/helmpath"
@ -52,18 +56,10 @@ func (t *TestHTTPGetter) Get(_ string, _ ...getter.Option) (*bytes.Buffer, error
var fakePluginB64 = "H4sIAAAAAAAAA+3SQUvDMBgG4Jz7K0LwapdvSxrwJig6mCKC5xHabBaXdDSt4L+3cQ56mV42ZPg+lw+SF5LwZmXf3OV206/rMGEnIgdG6zTJaDmee4y01FOlZpqGHJGZSsb1qS401sfOtpyz0FTup9xv+2dqNep/N/IP6zdHPSMVXCh1sH8yhtGMDBUFFTL1r4iIcXnUWxzwz/sP1rsrLkbfQGTvro11E4ZlmcucRNZHu04py1OO73OVi2Vbb7td9vp7nXevtvsKRpGVjfc2VMP2xf3t4mH5tHi5mz8ub+bPk9JXIvvr5wMAAAAAAAAAAAAAAAAAAAAAnLVPqwHcXQAoAAA="
func TestStripName(t *testing.T) {
if stripPluginName("fake-plugin-0.0.1.tar.gz") != "fake-plugin" {
t.Error("name does not match expected value")
}
if stripPluginName("fake-plugin-0.0.1.tgz") != "fake-plugin" {
t.Error("name does not match expected value")
}
if stripPluginName("fake-plugin.tgz") != "fake-plugin" {
t.Error("name does not match expected value")
}
if stripPluginName("fake-plugin.tar.gz") != "fake-plugin" {
t.Error("name does not match expected value")
}
assert.Equal(t, "fake-plugin", stripPluginName("fake-plugin-0.0.1.tar.gz"), "name does not match expected value")
assert.Equal(t, "fake-plugin", stripPluginName("fake-plugin-0.0.1.tgz"), "name does not match expected value")
assert.Equal(t, "fake-plugin", stripPluginName("fake-plugin.tgz"), "name does not match expected value")
assert.Equal(t, "fake-plugin", stripPluginName("fake-plugin.tar.gz"), "name does not match expected value")
}
func mockArchiveServer() *httptest.Server {
@ -71,10 +67,10 @@ func mockArchiveServer() *httptest.Server {
if !strings.HasSuffix(r.URL.Path, ".tar.gz") {
w.Header().Add("Content-Type", "text/html")
fmt.Fprintln(w, "broken")
return
} else {
w.Header().Add("Content-Type", "application/gzip")
fmt.Fprintln(w, "test")
}
w.Header().Add("Content-Type", "application/gzip")
fmt.Fprintln(w, "test")
}))
}
@ -85,45 +81,29 @@ func TestHTTPInstaller(t *testing.T) {
defer srv.Close()
source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0o755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
require.NoErrorf(t, os.MkdirAll(helmpath.DataPath("plugins"), 0o755), "Could not create %s", helmpath.DataPath("plugins"))
i, err := NewForSource(source, "0.0.1")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
// ensure a HTTPInstaller was returned
httpInstaller, ok := i.(*HTTPInstaller)
if !ok {
t.Fatal("expected a HTTPInstaller")
}
require.True(t, ok, "expected a HTTPInstaller")
// inject fake http client responding with minimal plugin tarball
mockTgz, err := base64.StdEncoding.DecodeString(fakePluginB64)
if err != nil {
t.Fatalf("Could not decode fake tgz plugin: %s", err)
}
require.NoError(t, err, "Could not decode fake tgz plugin")
httpInstaller.getter = &TestHTTPGetter{
MockResponse: bytes.NewBuffer(mockTgz),
}
// install the plugin
if err := Install(i); err != nil {
t.Fatal(err)
}
if i.Path() != helmpath.DataPath("plugins", "fake-plugin") {
t.Fatalf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path())
}
require.NoError(t, Install(i))
require.Equal(t, helmpath.DataPath("plugins", "fake-plugin"), i.Path(), "expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path())
// Install again to test plugin exists error
if err := Install(i); err == nil {
t.Fatal("expected error for plugin exists, got none")
} else if err.Error() != "plugin already exists" {
t.Fatalf("expected error for plugin exists, got (%v)", err)
}
require.EqualErrorf(t, Install(i), "plugin already exists", "expected error for plugin exists")
}
func TestHTTPInstallerNonExistentVersion(t *testing.T) {
@ -132,20 +112,14 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) {
defer srv.Close()
source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0o755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
require.NoErrorf(t, os.MkdirAll(helmpath.DataPath("plugins"), 0o755), "Could not create %s", helmpath.DataPath("plugins"))
i, err := NewForSource(source, "0.0.2")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
// ensure a HTTPInstaller was returned
httpInstaller, ok := i.(*HTTPInstaller)
if !ok {
t.Fatal("expected a HTTPInstaller")
}
require.True(t, ok, "expected a HTTPInstaller")
// inject fake http client responding with error
httpInstaller.getter = &TestHTTPGetter{
@ -153,9 +127,7 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) {
}
// attempt to install the plugin
if err := Install(i); err == nil {
t.Fatal("expected error from http client")
}
require.Error(t, Install(i), "expected error from http client")
}
func TestHTTPInstallerUpdate(t *testing.T) {
@ -164,43 +136,29 @@ func TestHTTPInstallerUpdate(t *testing.T) {
source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
ensure.HelmHome(t)
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0o755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
require.NoErrorf(t, os.MkdirAll(helmpath.DataPath("plugins"), 0o755), "Could not create %s", helmpath.DataPath("plugins"))
i, err := NewForSource(source, "0.0.1")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
// ensure a HTTPInstaller was returned
httpInstaller, ok := i.(*HTTPInstaller)
if !ok {
t.Fatal("expected a HTTPInstaller")
}
require.True(t, ok, "expected a HTTPInstaller")
// inject fake http client responding with minimal plugin tarball
mockTgz, err := base64.StdEncoding.DecodeString(fakePluginB64)
if err != nil {
t.Fatalf("Could not decode fake tgz plugin: %s", err)
}
require.NoError(t, err, "Could not decode fake tgz plugin")
httpInstaller.getter = &TestHTTPGetter{
MockResponse: bytes.NewBuffer(mockTgz),
}
// install the plugin before updating
if err := Install(i); err != nil {
t.Fatal(err)
}
if i.Path() != helmpath.DataPath("plugins", "fake-plugin") {
t.Fatalf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path())
}
require.NoError(t, Install(i))
require.Equal(t, helmpath.DataPath("plugins", "fake-plugin"), i.Path(), "expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path())
// Update plugin, should fail because it is not implemented
if err := Update(i); err == nil {
t.Fatal("update method not implemented for http installer")
}
require.Error(t, Update(i), "update method not implemented for http installer")
}
func TestExtract(t *testing.T) {
@ -229,12 +187,9 @@ func TestExtract(t *testing.T) {
Mode: file.Mode,
Size: int64(len(file.Body)),
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if _, err := tw.Write([]byte(file.Body)); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.WriteHeader(hdr))
_, err := tw.Write([]byte(file.Body))
require.NoError(t, err)
}
// Add pax global headers. This should be ignored.
@ -242,59 +197,46 @@ func TestExtract(t *testing.T) {
// Details are in the internal Go function for the tar packaged named
// allowedFormats. For a TypeXHeader it will return a message stating
// "cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"
if err := tw.WriteHeader(&tar.Header{
require.NoError(t, tw.WriteHeader(&tar.Header{
Name: "pax_global_header",
Typeflag: tar.TypeXGlobalHeader,
}); err != nil {
t.Fatal(err)
}
}))
if err := tw.Close(); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.Close())
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
if _, err := gz.Write(tarbuf.Bytes()); err != nil {
t.Fatal(err)
}
_, err := gz.Write(tarbuf.Bytes())
require.NoError(t, err)
gz.Close()
// END tarball creation
extractor, err := NewExtractor(source)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if err = extractor.Extract(&buf, tempDir); err != nil {
t.Fatalf("Did not expect error but got error: %v", err)
}
require.NoErrorf(t, extractor.Extract(&buf, tempDir), "Did not expect error")
// Calculate expected permissions after umask is applied
expectedPluginYAMLPerm := os.FileMode(0o600 &^ currentUmask)
expectedReadmePerm := os.FileMode(0o777 &^ currentUmask)
pluginYAMLFullPath := filepath.Join(tempDir, "plugin.yaml")
if info, err := os.Stat(pluginYAMLFullPath); err != nil {
if errors.Is(err, fs.ErrNotExist) {
t.Fatalf("Expected %s to exist but doesn't", pluginYAMLFullPath)
}
t.Fatal(err)
} else if info.Mode().Perm() != expectedPluginYAMLPerm {
t.Fatalf("Expected %s to have %o mode but has %o (umask: %o)",
pluginYAMLFullPath, expectedPluginYAMLPerm, info.Mode().Perm(), currentUmask)
info, err := os.Stat(pluginYAMLFullPath)
if err != nil {
require.NotErrorIs(t, err, fs.ErrNotExist, "Expected %s to exist but doesn't", pluginYAMLFullPath)
}
require.NoError(t, err)
require.Equalf(t, expectedPluginYAMLPerm, info.Mode().Perm(), "Expected %s to have %o mode but has %o (umask: %o)",
pluginYAMLFullPath, expectedPluginYAMLPerm, info.Mode().Perm(), currentUmask)
readmeFullPath := filepath.Join(tempDir, "README.md")
if info, err := os.Stat(readmeFullPath); err != nil {
if errors.Is(err, fs.ErrNotExist) {
t.Fatalf("Expected %s to exist but doesn't", readmeFullPath)
}
t.Fatal(err)
} else if info.Mode().Perm() != expectedReadmePerm {
t.Fatalf("Expected %s to have %o mode but has %o (umask: %o)",
readmeFullPath, expectedReadmePerm, info.Mode().Perm(), currentUmask)
info, err = os.Stat(readmeFullPath)
if err != nil {
require.NotErrorIs(t, err, fs.ErrNotExist, "Expected %s to exist but doesn't", readmeFullPath)
}
require.NoError(t, err)
require.Equalf(t, expectedReadmePerm, info.Mode().Perm(), "Expected %s to have %o mode but has %o (umask: %o)",
readmeFullPath, expectedReadmePerm, info.Mode().Perm(), currentUmask)
}
func TestCleanJoin(t *testing.T) {
@ -313,16 +255,15 @@ func TestCleanJoin(t *testing.T) {
{"foo\\bar.txt", "/tmp/foo/bar.txt", false},
{"c:\\foo\\bar.txt", "", true},
} {
out, err := cleanJoin("/tmp", fixture.path)
if err != nil {
if !fixture.expectError {
t.Errorf("Test %d: Path was not cleaned: %s", i, err)
t.Run(strconv.Itoa(i), func(t *testing.T) {
out, err := cleanJoin("/tmp", fixture.path)
if fixture.expectError {
require.Error(t, err, "Test %d: Path was not cleaned", i)
} else {
require.NoError(t, err)
assert.Equal(t, fixture.expect, out, "Test %d: Expected %q but got %q", i, fixture.expect, out)
}
continue
}
if fixture.expect != out {
t.Errorf("Test %d: Expected %q but got %q", i, fixture.expect, out)
}
})
}
}
@ -336,14 +277,11 @@ func TestMediaTypeToExtension(t *testing.T) {
"application/json": false,
} {
ext, ok := mediaTypeToExtension(mt)
if ok != shouldPass {
t.Errorf("Media type %q failed test", mt)
}
if shouldPass && ext == "" {
t.Error("Expected an extension but got empty string")
}
if !shouldPass && ext != "" {
t.Error("Expected extension to be empty for unrecognized type")
assert.Equal(t, shouldPass, ok, "Media type %q failed test", mt)
if shouldPass {
assert.NotEmpty(t, ext, "Expected an extension but got empty string for media type %q", mt)
} else {
assert.Empty(t, ext, "Expected extension to be empty for unrecognized media type %q", mt)
}
}
}
@ -377,55 +315,41 @@ func TestExtractWithNestedDirectories(t *testing.T) {
Mode: file.Mode,
Size: int64(len(file.Body)),
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.WriteHeader(hdr))
if file.TypeFlag == tar.TypeReg {
if _, err := tw.Write([]byte(file.Body)); err != nil {
t.Fatal(err)
}
_, err := tw.Write([]byte(file.Body))
require.NoError(t, err)
}
}
if err := tw.Close(); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.Close())
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
if _, err := gz.Write(tarbuf.Bytes()); err != nil {
t.Fatal(err)
}
_, err := gz.Write(tarbuf.Bytes())
require.NoError(t, err)
gz.Close()
extractor, err := NewExtractor(source)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
// First extraction
if err = extractor.Extract(&buf, tempDir); err != nil {
t.Fatalf("First extraction failed: %v", err)
}
require.NoError(t, extractor.Extract(&buf, tempDir), "First extraction failed")
// Verify nested structure was created
nestedFile := filepath.Join(tempDir, "docs", "examples", "example1.yaml")
if _, err := os.Stat(nestedFile); err != nil {
t.Fatalf("Expected nested file %s to exist but got error: %v", nestedFile, err)
}
_, err = os.Stat(nestedFile)
require.NoErrorf(t, err, "Expected nested file %s to exist", nestedFile)
// Reset buffer for second extraction
buf.Reset()
gz = gzip.NewWriter(&buf)
if _, err := gz.Write(tarbuf.Bytes()); err != nil {
t.Fatal(err)
}
_, err = gz.Write(tarbuf.Bytes())
require.NoError(t, err)
gz.Close()
// Second extraction to same directory (should not fail)
if err = extractor.Extract(&buf, tempDir); err != nil {
t.Fatalf("Second extraction to existing directory failed: %v", err)
}
require.NoErrorf(t, extractor.Extract(&buf, tempDir), "Second extraction to existing directory failed")
}
func TestExtractWithExistingDirectory(t *testing.T) {
@ -434,15 +358,11 @@ func TestExtractWithExistingDirectory(t *testing.T) {
// Pre-create the cache directory structure
cacheDir := filepath.Join(tempDir, "cache")
if err := os.MkdirAll(filepath.Join(cacheDir, "existing", "dir"), 0o755); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(filepath.Join(cacheDir, "existing", "dir"), 0o755))
// Create a file in the existing directory
existingFile := filepath.Join(cacheDir, "existing", "file.txt")
if err := os.WriteFile(existingFile, []byte("existing content"), 0o644); err != nil {
t.Fatal(err)
}
require.NoError(t, os.WriteFile(existingFile, []byte("existing content"), 0o644))
// Write a tarball
var tarbuf bytes.Buffer
@ -466,47 +386,35 @@ func TestExtractWithExistingDirectory(t *testing.T) {
Mode: file.Mode,
Size: int64(len(file.Body)),
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.WriteHeader(hdr))
if file.TypeFlag == tar.TypeReg {
if _, err := tw.Write([]byte(file.Body)); err != nil {
t.Fatal(err)
}
_, err := tw.Write([]byte(file.Body))
require.NoError(t, err)
}
}
if err := tw.Close(); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.Close())
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
if _, err := gz.Write(tarbuf.Bytes()); err != nil {
t.Fatal(err)
}
_, err := gz.Write(tarbuf.Bytes())
require.NoError(t, err)
gz.Close()
extractor, err := NewExtractor(source)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
// Extract to directory with existing content
if err = extractor.Extract(&buf, cacheDir); err != nil {
t.Fatalf("Extraction to directory with existing content failed: %v", err)
}
require.NoErrorf(t, extractor.Extract(&buf, cacheDir), "Extraction to directory with existing content failed")
// Verify new file was created
newFile := filepath.Join(cacheDir, "existing", "dir", "newfile.txt")
if _, err := os.Stat(newFile); err != nil {
t.Fatalf("Expected new file %s to exist but got error: %v", newFile, err)
}
_, err = os.Stat(newFile)
require.NoErrorf(t, err, "Expected new file %s to exist but got error", newFile)
// Verify existing file is still there
if _, err := os.Stat(existingFile); err != nil {
t.Fatalf("Expected existing file %s to still exist but got error: %v", existingFile, err)
}
_, err = os.Stat(existingFile)
require.NoErrorf(t, err, "Expected existing file %s to still exist", existingFile)
}
func TestExtractPluginInSubdirectory(t *testing.T) {
@ -536,25 +444,19 @@ func TestExtractPluginInSubdirectory(t *testing.T) {
Mode: file.Mode,
Size: int64(len(file.Body)),
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.WriteHeader(hdr))
if file.TypeFlag == tar.TypeReg {
if _, err := tw.Write([]byte(file.Body)); err != nil {
t.Fatal(err)
}
_, err := tw.Write([]byte(file.Body))
require.NoError(t, err)
}
}
if err := tw.Close(); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.Close())
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
if _, err := gz.Write(tarbuf.Bytes()); err != nil {
t.Fatal(err)
}
_, err := gz.Write(tarbuf.Bytes())
require.NoError(t, err)
gz.Close()
// Test the installer
@ -573,24 +475,16 @@ func TestExtractPluginInSubdirectory(t *testing.T) {
// Ensure the destination directory doesn't exist
// (In a real scenario, this is handled by installer.Install() wrapper)
destPath := installer.Path()
if err := os.RemoveAll(destPath); err != nil {
t.Fatalf("Failed to clean destination path: %v", err)
}
require.NoErrorf(t, os.RemoveAll(destPath), "Failed to clean destination path")
// Install should handle the subdirectory correctly
if err := installer.Install(); err != nil {
t.Fatalf("Failed to install plugin with subdirectory: %v", err)
}
require.NoErrorf(t, installer.Install(), "Failed to install plugin with subdirectory")
// The plugin should be installed from the subdirectory
// Check that detectPluginRoot found the correct location
pluginRoot, err := detectPluginRoot(tempDir)
if err != nil {
t.Fatalf("Failed to detect plugin root: %v", err)
}
require.NoError(t, err, "Failed to detect plugin root")
expectedRoot := filepath.Join(tempDir, "my-plugin")
if pluginRoot != expectedRoot {
t.Errorf("Expected plugin root to be %s but got %s", expectedRoot, pluginRoot)
}
assert.Equal(t, expectedRoot, pluginRoot, "Expected plugin root to be %s but got %s", expectedRoot, pluginRoot)
}

@ -15,33 +15,25 @@ limitations under the License.
package installer
import "testing"
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsRemoteHTTPArchive(t *testing.T) {
srv := mockArchiveServer()
defer srv.Close()
source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
if isRemoteHTTPArchive("/not/a/URL") {
t.Error("Expected non-URL to return false")
}
assert.False(t, isRemoteHTTPArchive("/not/a/URL"), "Expected non-URL to return false")
// URLs with valid archive extensions are considered valid archives
// even if the server is unreachable (optimization to avoid unnecessary HTTP requests)
if !isRemoteHTTPArchive("https://127.0.0.1:123/fake/plugin-1.2.3.tgz") {
t.Error("URL with .tgz extension should be considered a valid archive")
}
assert.True(t, isRemoteHTTPArchive("https://127.0.0.1:123/fake/plugin-1.2.3.tgz"), "URL with .tgz extension should be considered a valid archive")
// Test with invalid extension and unreachable server
if isRemoteHTTPArchive("https://127.0.0.1:123/fake/plugin-1.2.3.notanarchive") {
t.Error("Bad URL without valid extension should not succeed")
}
if !isRemoteHTTPArchive(source) {
t.Errorf("Expected %q to be a valid archive URL", source)
}
if isRemoteHTTPArchive(source + "-not-an-extension") {
t.Error("Expected media type match to fail")
}
assert.False(t, isRemoteHTTPArchive("https://127.0.0.1:123/fake/plugin-1.2.3.notanarchive"), "Bad URL without valid extension should not succeed")
assert.True(t, isRemoteHTTPArchive(source), "Expected %q to be a valid archive URL", source)
assert.False(t, isRemoteHTTPArchive(source+"-not-an-extension"), "Expected media type match to fail")
}

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"bytes"

@ -13,17 +13,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/internal/test/ensure"
"helm.sh/helm/v4/pkg/helmpath"
)
@ -34,40 +35,23 @@ func TestLocalInstaller(t *testing.T) {
ensure.HelmHome(t)
// Make a temp dir
tdir := t.TempDir()
if err := os.WriteFile(filepath.Join(tdir, "plugin.yaml"), []byte{}, 0o644); err != nil {
t.Fatal(err)
}
require.NoError(t, os.WriteFile(filepath.Join(tdir, "plugin.yaml"), []byte{}, 0o644))
source := "../testdata/plugdir/good/echo-v1"
i, err := NewForSource(source, "")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
if err := Install(i); err != nil {
t.Fatal(err)
}
require.NoError(t, Install(i))
if i.Path() != helmpath.DataPath("plugins", "echo-v1") {
t.Fatalf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path())
}
require.Equal(t, helmpath.DataPath("plugins", "echo-v1"), i.Path(), "expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path())
os.RemoveAll(filepath.Dir(helmpath.DataPath())) // helmpath.DataPath is like /tmp/helm013130971/helm
}
func TestLocalInstallerNotAFolder(t *testing.T) {
source := "../testdata/plugdir/good/echo-v1/plugin.yaml"
i, err := NewForSource(source, "")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = Install(i)
if err == nil {
t.Fatal("expected error")
}
if !errors.Is(err, ErrPluginNotADirectory) {
t.Fatalf("expected error to equal: %q", err)
}
require.NoError(t, err)
require.ErrorIs(t, Install(i), ErrPluginNotADirectory)
}
func TestLocalInstallerTarball(t *testing.T) {
@ -97,53 +81,31 @@ func TestLocalInstallerTarball(t *testing.T) {
Mode: file.Mode,
Size: int64(len(file.Body)),
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if _, err := tw.Write([]byte(file.Body)); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.WriteHeader(hdr))
_, err := tw.Write([]byte(file.Body))
require.NoError(t, err)
}
if err := tw.Close(); err != nil {
t.Fatal(err)
}
if err := gw.Close(); err != nil {
t.Fatal(err)
}
require.NoError(t, tw.Close())
require.NoError(t, gw.Close())
// Write tarball to file
if err := os.WriteFile(tarballPath, buf.Bytes(), 0o644); err != nil {
t.Fatal(err)
}
require.NoError(t, os.WriteFile(tarballPath, buf.Bytes(), 0o644))
// Test installation
i, err := NewForSource(tarballPath, "")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
// Verify it's detected as LocalInstaller
localInstaller, ok := i.(*LocalInstaller)
if !ok {
t.Fatal("expected LocalInstaller")
}
if !localInstaller.isArchive {
t.Fatal("expected isArchive to be true")
}
if err := Install(i); err != nil {
t.Fatal(err)
}
require.True(t, ok, "expected LocalInstaller")
require.True(t, localInstaller.isArchive, "expected isArchive to be true")
require.NoError(t, Install(i))
expectedPath := helmpath.DataPath("plugins", "test-plugin")
if i.Path() != expectedPath {
t.Fatalf("expected path %q, got %q", expectedPath, i.Path())
}
require.Equal(t, expectedPath, i.Path(), "expected path %q, got %q", expectedPath, i.Path())
// Verify plugin was installed
if _, err := os.Stat(i.Path()); err != nil {
t.Fatalf("plugin not found at %s: %v", i.Path(), err)
}
_, err = os.Stat(i.Path())
require.NoErrorf(t, err, "plugin not found at %s", i.Path())
}

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"archive/tar"
@ -64,12 +64,9 @@ command: "$HELM_PLUGIN_DIR/bin/%s"
Size: int64(len(pluginYAML)),
Typeflag: tar.TypeReg,
}
if err := tarWriter.WriteHeader(header); err != nil {
t.Fatal(err)
}
if _, err := tarWriter.Write([]byte(pluginYAML)); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(header))
_, err := tarWriter.Write([]byte(pluginYAML))
require.NoError(t, err)
// Add bin directory
dirHeader := &tar.Header{
@ -77,9 +74,7 @@ command: "$HELM_PLUGIN_DIR/bin/%s"
Mode: 0o755,
Typeflag: tar.TypeDir,
}
if err := tarWriter.WriteHeader(dirHeader); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(dirHeader))
// Add executable
execContent := fmt.Sprintf("#!/bin/sh\necho '%s test plugin'", pluginName)
@ -89,12 +84,9 @@ command: "$HELM_PLUGIN_DIR/bin/%s"
Size: int64(len(execContent)),
Typeflag: tar.TypeReg,
}
if err := tarWriter.WriteHeader(execHeader); err != nil {
t.Fatal(err)
}
if _, err := tarWriter.Write([]byte(execContent)); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(execHeader))
_, err = tarWriter.Write([]byte(execContent))
require.NoError(t, err)
tarWriter.Close()
gzWriter.Close()
@ -135,9 +127,7 @@ func mockOCIRegistryWithArtifactType(t *testing.T, pluginName string) (*httptest
}
manifestData, err := json.Marshal(manifest)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
manifestDigest := fmt.Sprintf("sha256:%x", sha256Sum(manifestData))
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -175,9 +165,7 @@ func mockOCIRegistryWithArtifactType(t *testing.T, pluginName string) (*httptest
// Parse server URL to get host:port format for OCI reference
serverURL, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
registryHost := serverURL.Host
return server, registryHost
@ -240,42 +228,20 @@ func TestNewOCIInstaller(t *testing.T) {
installer, err := NewOCIInstaller(tt.source)
if tt.expectError {
if err == nil {
t.Error("expected error but got none")
}
return
}
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
// Check all fields thoroughly
if installer.PluginName != tt.expectName {
t.Errorf("expected plugin name %s, got %s", tt.expectName, installer.PluginName)
}
if installer.Source != tt.source {
t.Errorf("expected source %s, got %s", tt.source, installer.Source)
}
if installer.CacheDir == "" {
t.Error("expected non-empty cache directory")
}
if !strings.Contains(installer.CacheDir, "plugins") {
t.Errorf("expected cache directory to contain 'plugins', got %s", installer.CacheDir)
}
if installer.settings == nil {
t.Error("expected settings to be initialized")
}
// Check that Path() method works
expectedPath := helmpath.DataPath("plugins", tt.expectName)
if installer.Path() != expectedPath {
t.Errorf("expected path %s, got %s", expectedPath, installer.Path())
require.Error(t, err)
} else {
require.NoError(t, err)
// Check all fields thoroughly
assert.Equal(t, tt.expectName, installer.PluginName, "expected plugin name %s, got %s", tt.expectName, installer.PluginName)
assert.Equal(t, tt.source, installer.Source, "expected source %s, got %s", tt.source, installer.Source)
assert.NotEmpty(t, installer.CacheDir, "expected non-empty cache directory")
assert.Contains(t, installer.CacheDir, "plugins", "expected cache directory to contain 'plugins', got %s", installer.CacheDir)
assert.NotNil(t, installer.settings, "expected settings to be initialized")
// Check that Path() method works
expectedPath := helmpath.DataPath("plugins", tt.expectName)
assert.Equal(t, expectedPath, installer.Path(), "expected path %s, got %s", expectedPath, installer.Path())
}
})
}
@ -311,9 +277,7 @@ func TestOCIInstaller_Path(t *testing.T) {
}
path := installer.Path()
if path != tt.expectPath {
t.Errorf("expected path %s, got %s", tt.expectPath, path)
}
assert.Equal(t, tt.expectPath, path, "expected path %s, got %s", tt.expectPath, path)
})
}
}
@ -331,39 +295,30 @@ func TestOCIInstaller_Install(t *testing.T) {
// Test with plain HTTP (since test server uses HTTP)
installer, err := NewOCIInstaller(source, getter.WithPlainHTTP(true))
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
require.NoError(t, err)
// The OCI installer uses helmpath.DataPath, which is isolated by ensure.HelmHome(t)
actualPath := installer.Path()
t.Logf("Installer will use path: %s", actualPath)
// Install the plugin
if err := Install(installer); err != nil {
t.Fatalf("Expected installation to succeed, got error: %v", err)
}
require.NoErrorf(t, Install(installer), "Expected installation to succeed")
// Verify plugin was installed to the correct location
if !isPlugin(actualPath) {
t.Errorf("Expected plugin directory %s to contain plugin.yaml", actualPath)
}
assert.Truef(t, isPlugin(actualPath), "Expected plugin directory %s to contain plugin.yaml", actualPath)
// Debug: list what was actually created
if entries, err := os.ReadDir(actualPath); err != nil {
t.Fatalf("Could not read plugin directory %s: %v", actualPath, err)
} else {
t.Logf("Plugin directory %s contains:", actualPath)
for _, entry := range entries {
t.Logf(" - %s", entry.Name())
}
entries, err := os.ReadDir(actualPath)
require.NoError(t, err, "Could not read plugin directory %s", actualPath)
t.Logf("Plugin directory %s contains:", actualPath)
for _, entry := range entries {
t.Logf(" - %s", entry.Name())
}
// Verify the plugin.yaml file exists and is valid
pluginFile := filepath.Join(actualPath, "plugin.yaml")
if _, err := os.Stat(pluginFile); err != nil {
t.Errorf("Expected plugin.yaml to exist, got error: %v", err)
}
_, err = os.Stat(pluginFile)
assert.NoErrorf(t, err, "Expected plugin.yaml to exist")
}
func TestOCIInstaller_Install_WithGetterOptions(t *testing.T) {
@ -404,11 +359,9 @@ func TestOCIInstaller_Install_WithGetterOptions(t *testing.T) {
source := fmt.Sprintf("oci://%s/%s:latest", registryHost, tc.pluginName)
installer, err := NewOCIInstaller(source, tc.options...)
if err != nil {
if !tc.wantErr {
t.Fatalf("Expected no error creating installer, got %v", err)
}
return
if !tc.wantErr {
require.NoError(t, err, "Expected no error creating installer")
}
// The installer now uses our isolated test directory
@ -419,7 +372,7 @@ func TestOCIInstaller_Install_WithGetterOptions(t *testing.T) {
if tc.wantErr {
require.Error(t, err, "Expected installation to fail, but it succeeded")
} else {
require.NoError(t, err, "Expected installation to succeed, got error: %v", err)
require.NoError(t, err, "Expected installation to succeed")
// Verify plugin was installed to the actual path
assert.True(t, isPlugin(actualPath), "Expected plugin directory %s to contain plugin.yaml", actualPath)
}
@ -437,27 +390,16 @@ func TestOCIInstaller_Install_AlreadyExists(t *testing.T) {
source := fmt.Sprintf("oci://%s/%s:latest", registryHost, pluginName)
installer, err := NewOCIInstaller(source, getter.WithPlainHTTP(true))
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
require.NoError(t, err)
// First install should succeed
if err := Install(installer); err != nil {
t.Fatalf("Expected first installation to succeed, got error: %v", err)
}
require.NoErrorf(t, Install(installer), "Expected first installation to succeed")
// Verify plugin was installed
if !isPlugin(installer.Path()) {
t.Errorf("Expected plugin directory %s to contain plugin.yaml", installer.Path())
}
assert.Truef(t, isPlugin(installer.Path()), "Expected plugin directory %s to contain plugin.yaml", installer.Path())
// Second install should fail with "plugin already exists"
err = Install(installer)
if err == nil {
t.Error("Expected error when installing plugin that already exists")
} else if !strings.Contains(err.Error(), "plugin already exists") {
t.Errorf("Expected 'plugin already exists' error, got: %v", err)
}
assert.ErrorContains(t, Install(installer), "plugin already exists")
}
func TestOCIInstaller_Update(t *testing.T) {
@ -470,38 +412,23 @@ func TestOCIInstaller_Update(t *testing.T) {
source := fmt.Sprintf("oci://%s/%s:latest", registryHost, pluginName)
installer, err := NewOCIInstaller(source, getter.WithPlainHTTP(true))
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
require.NoError(t, err)
// Test update when plugin does not exist - should fail
err = Update(installer)
if err == nil {
t.Error("Expected error when updating plugin that does not exist")
} else if !strings.Contains(err.Error(), "plugin does not exist") {
t.Errorf("Expected 'plugin does not exist' error, got: %v", err)
}
require.ErrorContains(t, Update(installer), "plugin does not exist")
// Install plugin first
if err := Install(installer); err != nil {
t.Fatalf("Expected installation to succeed, got error: %v", err)
}
require.NoErrorf(t, Install(installer), "Expected installation to succeed")
// Verify plugin was installed
if !isPlugin(installer.Path()) {
t.Errorf("Expected plugin directory %s to contain plugin.yaml", installer.Path())
}
assert.Truef(t, isPlugin(installer.Path()), "Expected plugin directory %s to contain plugin.yaml", installer.Path())
// Test update when plugin exists - should succeed
// For OCI, Update() removes old version and reinstalls
if err := Update(installer); err != nil {
t.Errorf("Expected update to succeed, got error: %v", err)
}
require.NoErrorf(t, Update(installer), "Expected update to succeed")
// Verify plugin is still installed after update
if !isPlugin(installer.Path()) {
t.Errorf("Expected plugin directory %s to contain plugin.yaml after update", installer.Path())
}
assert.Truef(t, isPlugin(installer.Path()), "Expected plugin directory %s to contain plugin.yaml after update", installer.Path())
}
func TestOCIInstaller_Install_ComponentExtraction(t *testing.T) {
@ -513,35 +440,26 @@ func TestOCIInstaller_Install_ComponentExtraction(t *testing.T) {
pluginData := createTestPluginTarGz(t, pluginName)
// Test extraction
err := extractTarGz(bytes.NewReader(pluginData), tempDir)
if err != nil {
t.Fatalf("Failed to extract plugin: %v", err)
}
require.NoError(t, extractTarGz(bytes.NewReader(pluginData), tempDir), "Failed to extract plugin")
// Verify plugin.yaml exists
pluginYAMLPath := filepath.Join(tempDir, "plugin.yaml")
if _, err := os.Stat(pluginYAMLPath); os.IsNotExist(err) {
t.Error("plugin.yaml not found after extraction")
}
_, err := os.Stat(pluginYAMLPath)
assert.False(t, os.IsNotExist(err), "plugin.yaml not found after extraction")
// Verify bin directory exists
binPath := filepath.Join(tempDir, "bin")
if _, err := os.Stat(binPath); os.IsNotExist(err) {
t.Error("bin directory not found after extraction")
}
_, err = os.Stat(binPath)
assert.False(t, os.IsNotExist(err), "bin directory not found after extraction")
// Verify executable exists and has correct permissions
execPath := filepath.Join(tempDir, "bin", pluginName)
if info, err := os.Stat(execPath); err != nil {
t.Errorf("executable not found: %v", err)
} else if info.Mode()&0o111 == 0 {
t.Error("file is not executable")
}
info, err := os.Stat(execPath)
require.NoError(t, err, "executable not found")
assert.NotEqual(t, 0, info.Mode()&0o111, "file is not executable")
// Verify this would be recognized as a plugin
if !isPlugin(tempDir) {
t.Error("extracted directory is not a valid plugin")
}
assert.True(t, isPlugin(tempDir), "extracted directory is not a valid plugin")
}
func TestExtractTarGz(t *testing.T) {
@ -561,13 +479,10 @@ func TestExtractTarGz(t *testing.T) {
Typeflag: tar.TypeReg,
}
if err := tarWriter.WriteHeader(header); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(header))
if _, err := tarWriter.Write([]byte(testContent)); err != nil {
t.Fatal(err)
}
_, err := tarWriter.Write([]byte(testContent))
require.NoError(t, err)
// Add a test directory
dirHeader := &tar.Header{
@ -576,35 +491,25 @@ func TestExtractTarGz(t *testing.T) {
Typeflag: tar.TypeDir,
}
if err := tarWriter.WriteHeader(dirHeader); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(dirHeader))
tarWriter.Close()
gzWriter.Close()
// Test extraction
err := extractTarGz(bytes.NewReader(buf.Bytes()), tempDir)
if err != nil {
t.Errorf("extractTarGz failed: %v", err)
}
require.NoError(t, extractTarGz(bytes.NewReader(buf.Bytes()), tempDir), "extractTarGz failed")
// Verify extracted file
extractedFile := filepath.Join(tempDir, "test-file.txt")
content, err := os.ReadFile(extractedFile)
if err != nil {
t.Errorf("failed to read extracted file: %v", err)
}
require.NoError(t, err, "failed to read extracted file")
if string(content) != testContent {
t.Errorf("expected content %s, got %s", testContent, string(content))
}
assert.Equal(t, testContent, string(content), "expected content %s, got %s", testContent, string(content))
// Verify extracted directory
extractedDir := filepath.Join(tempDir, "test-dir")
if _, err := os.Stat(extractedDir); os.IsNotExist(err) {
t.Errorf("extracted directory does not exist: %s", extractedDir)
}
_, err = os.Stat(extractedDir)
assert.Falsef(t, os.IsNotExist(err), "extracted directory does not exist: %s", extractedDir)
}
func TestExtractTarGz_InvalidGzip(t *testing.T) {
@ -612,10 +517,7 @@ func TestExtractTarGz_InvalidGzip(t *testing.T) {
// Test with invalid gzip data
invalidGzipData := []byte("not gzip data")
err := extractTarGz(bytes.NewReader(invalidGzipData), tempDir)
if err == nil {
t.Error("expected error for invalid gzip data")
}
assert.Error(t, extractTarGz(bytes.NewReader(invalidGzipData), tempDir), "expected error for invalid gzip data")
}
func TestExtractTar_UnknownFileType(t *testing.T) {
@ -634,13 +536,10 @@ func TestExtractTar_UnknownFileType(t *testing.T) {
Typeflag: tar.TypeReg,
}
if err := tarWriter.WriteHeader(header); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(header))
if _, err := tarWriter.Write([]byte(testContent)); err != nil {
t.Fatal(err)
}
_, err := tarWriter.Write([]byte(testContent))
require.NoError(t, err)
// Test unknown file type
unknownHeader := &tar.Header{
@ -649,21 +548,12 @@ func TestExtractTar_UnknownFileType(t *testing.T) {
Typeflag: tar.TypeSymlink, // Use a type that's not handled
}
if err := tarWriter.WriteHeader(unknownHeader); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(unknownHeader))
tarWriter.Close()
// Test extraction - should fail due to unknown type
err := extractTar(bytes.NewReader(buf.Bytes()), tempDir)
if err == nil {
t.Error("expected error for unknown tar file type")
}
if !strings.Contains(err.Error(), "unknown type") {
t.Errorf("expected 'unknown type' error, got: %v", err)
}
assert.ErrorContains(t, extractTar(bytes.NewReader(buf.Bytes()), tempDir), "unknown type")
}
func TestExtractTar_SuccessfulExtraction(t *testing.T) {
@ -686,32 +576,22 @@ func TestExtractTar_SuccessfulExtraction(t *testing.T) {
Typeflag: tar.TypeReg,
}
if err := tarWriter.WriteHeader(header); err != nil {
t.Fatal(err)
}
require.NoError(t, tarWriter.WriteHeader(header))
if _, err := tarWriter.Write([]byte(testContent)); err != nil {
t.Fatal(err)
}
_, err := tarWriter.Write([]byte(testContent))
require.NoError(t, err)
tarWriter.Close()
// Test extraction
err := extractTar(bytes.NewReader(buf.Bytes()), tempDir)
if err != nil {
t.Errorf("extractTar failed: %v", err)
}
require.NoError(t, extractTar(bytes.NewReader(buf.Bytes()), tempDir), "extractTar failed")
// Verify the regular file was extracted
extractedFile := filepath.Join(tempDir, "test-file.txt")
content, err := os.ReadFile(extractedFile)
if err != nil {
t.Errorf("failed to read extracted file: %v", err)
}
require.NoError(t, err, "failed to read extracted file")
if string(content) != testContent {
t.Errorf("expected content %s, got %s", testContent, string(content))
}
assert.Equal(t, testContent, string(content), "expected content %s, got %s", testContent, string(content))
}
func TestOCIInstaller_Install_PlainHTTPOption(t *testing.T) {
@ -720,41 +600,25 @@ func TestOCIInstaller_Install_PlainHTTPOption(t *testing.T) {
// Test with PlainHTTP=false (default)
installer1, err := NewOCIInstaller(source)
if err != nil {
t.Fatalf("failed to create installer: %v", err)
}
if installer1.getter == nil {
t.Error("getter should be initialized")
}
require.NoError(t, err, "failed to create installer")
assert.NotNil(t, installer1.getter, "getter should be initialized")
// Test with PlainHTTP=true
installer2, err := NewOCIInstaller(source, getter.WithPlainHTTP(true))
if err != nil {
t.Fatalf("failed to create installer with PlainHTTP=true: %v", err)
}
if installer2.getter == nil {
t.Error("getter should be initialized with PlainHTTP=true")
}
require.NoError(t, err, "failed to create installer with PlainHTTP=true")
assert.NotNil(t, installer2.getter, "getter should be initialized with PlainHTTP=true")
// Both installers should have the same basic properties
if installer1.PluginName != installer2.PluginName {
t.Error("plugin names should match")
}
if installer1.Source != installer2.Source {
t.Error("sources should match")
}
assert.Equal(t, installer2.PluginName, installer1.PluginName, "plugin names should match")
assert.Equal(t, installer2.Source, installer1.Source, "sources should match")
// Test with multiple options
installer3, err := NewOCIInstaller(source,
getter.WithPlainHTTP(true),
getter.WithBasicAuth("user", "pass"),
)
if err != nil {
t.Fatalf("failed to create installer with multiple options: %v", err)
}
if installer3.getter == nil {
t.Error("getter should be initialized with multiple options")
}
require.NoError(t, err, "failed to create installer with multiple options")
assert.NotNil(t, installer3.getter, "getter should be initialized with multiple options")
}
func TestOCIInstaller_Install_ValidationErrors(t *testing.T) {
@ -789,12 +653,8 @@ func TestOCIInstaller_Install_ValidationErrors(t *testing.T) {
// Test the gzip validation logic that's used in the Install method
if len(tt.layerData) < 2 || tt.layerData[0] != 0x1f || tt.layerData[1] != 0x8b {
// This matches the validation in the Install method
if !tt.expectError {
t.Error("expected valid gzip data")
}
if !strings.Contains(tt.errorMsg, "is not a gzip compressed archive") {
t.Error("expected error message to contain 'is not a gzip compressed archive'")
}
assert.True(t, tt.expectError, "expected valid gzip data")
assert.Contains(t, tt.errorMsg, "is not a gzip compressed archive")
}
})
}

@ -19,6 +19,9 @@ import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDetectPluginRoot(t *testing.T) {
@ -73,26 +76,18 @@ func TestDetectPluginRoot(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dir := t.TempDir()
if err := tt.setup(dir); err != nil {
t.Fatalf("Setup failed: %v", err)
}
require.NoErrorf(t, tt.setup(dir), "Setup failed")
root, err := detectPluginRoot(dir)
if tt.expectError {
if err == nil {
t.Error("Expected error but got none")
}
assert.Error(t, err)
} else {
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
require.NoError(t, err)
expectedPath := dir
if tt.expectRoot != "." {
expectedPath = filepath.Join(dir, tt.expectRoot)
}
if root != expectedPath {
t.Errorf("Expected root %s but got %s", expectedPath, root)
}
assert.Equal(t, expectedPath, root, "Expected root %s but got %s", expectedPath, root)
}
})
}
@ -145,20 +140,14 @@ description: test`
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dir := t.TempDir()
if err := tt.setup(dir); err != nil {
t.Fatalf("Setup failed: %v", err)
}
require.NoErrorf(t, tt.setup(dir), "Setup failed")
pluginRoot := filepath.Join(dir, tt.pluginRoot)
err := validatePluginName(pluginRoot, tt.expectedName)
if tt.expectError {
if err == nil {
t.Error("Expected error but got none")
}
assert.Error(t, err)
} else {
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
assert.NoError(t, err)
}
})
}

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"errors"

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v4/internal/plugin/installer"
package installer
import (
"fmt"
@ -23,6 +23,7 @@ import (
"testing"
"github.com/Masterminds/vcs"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/internal/test/ensure"
"helm.sh/helm/v4/pkg/helmpath"
@ -52,9 +53,7 @@ func (r *testRepo) UpdateVersion(version string) error {
func TestVCSInstaller(t *testing.T) {
ensure.HelmHome(t)
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0o755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
require.NoErrorf(t, os.MkdirAll(helmpath.DataPath("plugins"), 0o755), "Could not create %s", helmpath.DataPath("plugins"))
source := "https://github.com/adamreese/helm-env"
testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo-v1")
@ -64,43 +63,27 @@ func TestVCSInstaller(t *testing.T) {
}
i, err := NewForSource(source, "~0.1.0")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
// ensure a VCSInstaller was returned
vcsInstaller, ok := i.(*VCSInstaller)
if !ok {
t.Fatal("expected a VCSInstaller")
}
require.True(t, ok, "expected a VCSInstaller")
// set the testRepo in the VCSInstaller
vcsInstaller.Repo = repo
if err := Install(i); err != nil {
t.Fatal(err)
}
if repo.current != "0.1.1" {
t.Fatalf("expected version '0.1.1', got %q", repo.current)
}
require.NoError(t, Install(i))
require.Equal(t, "0.1.1", repo.current, "expected version '0.1.1', got %q", repo.current)
expectedPath := helmpath.DataPath("plugins", "helm-env")
if i.Path() != expectedPath {
t.Fatalf("expected path %q, got %q", expectedPath, i.Path())
}
require.Equal(t, expectedPath, i.Path(), "expected path %q, got %q", expectedPath, i.Path())
// Install again to test plugin exists error
if err := Install(i); err == nil {
t.Fatal("expected error for plugin exists, got none")
} else if err.Error() != "plugin already exists" {
t.Fatalf("expected error for plugin exists, got (%v)", err)
}
require.EqualErrorf(t, Install(i), "plugin already exists", "expected error for plugin exists")
// Testing FindSource method, expect error because plugin code is not a cloned repository
if _, err := FindSource(i.Path()); err == nil {
t.Fatal("expected error for inability to find plugin source, got none")
} else if err.Error() != "cannot get information about plugin source" {
t.Fatalf("expected error for inability to find plugin source, got (%v)", err)
}
_, err = FindSource(i.Path())
require.Error(t, err, "expected error for inability to find plugin source, got none")
require.EqualErrorf(t, err, "cannot get information about plugin source", "expected error for inability to find plugin source")
}
func TestVCSInstallerNonExistentVersion(t *testing.T) {
@ -110,22 +93,17 @@ func TestVCSInstallerNonExistentVersion(t *testing.T) {
version := "0.2.0"
i, err := NewForSource(source, version)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
// ensure a VCSInstaller was returned
if _, ok := i.(*VCSInstaller); !ok {
t.Fatal("expected a VCSInstaller")
}
require.IsType(t, &VCSInstaller{}, i, "expected a VCSInstaller")
if err := Install(i); err == nil {
t.Fatal("expected error for version does not exists, got none")
} else if strings.Contains(err.Error(), "Could not resolve host: github.com") {
err = Install(i)
require.Error(t, err, "expected error for version does not exists, got none")
if strings.Contains(err.Error(), "Could not resolve host: github.com") {
t.Skip("Unable to run test without Internet access")
} else if err.Error() != fmt.Sprintf("requested version %q does not exist for plugin %q", version, source) {
t.Fatalf("expected error for version does not exists, got (%v)", err)
}
require.EqualErrorf(t, err, fmt.Sprintf("requested version %q does not exist for plugin %q", version, source), "expected error for version does not exists")
}
func TestVCSInstallerUpdate(t *testing.T) {
ensure.HelmHome(t)
@ -133,56 +111,34 @@ func TestVCSInstallerUpdate(t *testing.T) {
source := "https://github.com/adamreese/helm-env"
i, err := NewForSource(source, "")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
require.NoError(t, err)
// ensure a VCSInstaller was returned
if _, ok := i.(*VCSInstaller); !ok {
t.Fatal("expected a VCSInstaller")
}
require.IsType(t, &VCSInstaller{}, i, "expected a VCSInstaller")
if err := Update(i); err == nil {
t.Fatal("expected error for plugin does not exist, got none")
} else if err.Error() != "plugin does not exist" {
t.Fatalf("expected error for plugin does not exist, got (%v)", err)
}
require.EqualErrorf(t, Update(i), "plugin does not exist", "expected error for plugin does not exist")
// Install plugin before update
if err := Install(i); err != nil {
if strings.Contains(err.Error(), "Could not resolve host: github.com") {
t.Skip("Unable to run test without Internet access")
} else {
t.Fatal(err)
}
err = Install(i)
if err != nil && strings.Contains(err.Error(), "Could not resolve host: github.com") {
t.Skip("Unable to run test without Internet access")
}
require.NoError(t, err)
// Test FindSource method for positive result
pluginInfo, err := FindSource(i.Path())
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
vcsInstaller := pluginInfo.(*VCSInstaller)
repoRemote := vcsInstaller.Repo.Remote()
if repoRemote != source {
t.Fatalf("invalid source found, expected %q got %q", source, repoRemote)
}
require.Equal(t, source, repoRemote, "invalid source found, expected %q got %q", source, repoRemote)
// Update plugin
if err := Update(i); err != nil {
t.Fatal(err)
}
require.NoError(t, Update(i))
// Test update failure
if err := os.Remove(filepath.Join(vcsInstaller.Repo.LocalPath(), "plugin.yaml")); err != nil {
t.Fatal(err)
}
require.NoError(t, os.Remove(filepath.Join(vcsInstaller.Repo.LocalPath(), "plugin.yaml")))
// Testing update for error
if err := Update(vcsInstaller); err == nil {
t.Fatal("expected error for plugin modified, got none")
} else if err.Error() != "plugin repo was modified" {
t.Fatalf("expected error for plugin modified, got (%v)", err)
}
require.EqualErrorf(t, Update(vcsInstaller), "plugin repo was modified", "expected error for plugin modified")
}

@ -23,6 +23,9 @@ import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/internal/plugin"
"helm.sh/helm/v4/internal/test/ensure"
)
@ -37,29 +40,20 @@ func TestInstallWithOptions_VerifyMissingProvenance(t *testing.T) {
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install with verification enabled should fail when .prov is missing
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: "dummy"})
// Should fail with a missing provenance error
if err == nil {
t.Fatal("Expected installation to fail when .prov file is missing and verification is enabled")
}
if !strings.Contains(err.Error(), "no provenance file") {
t.Errorf("Expected 'no provenance file' in error message, got: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when .prov file is missing, got: %+v", result)
}
require.Error(t, err, "Expected installation to fail when .prov file is missing and verification is enabled")
require.ErrorContains(t, err, "no provenance file")
assert.Nil(t, result, "Expected nil verification result when .prov file is missing, got: %+v", result)
// Plugin should NOT be installed
if _, err := os.Stat(installer.Path()); !os.IsNotExist(err) {
t.Error("Plugin should not be installed when verification fails due to missing .prov")
}
_, err = os.Stat(installer.Path())
assert.True(t, os.IsNotExist(err), "Plugin should not be installed when verification fails due to missing .prov")
}
func TestInstallWithOptions_NoVerifyMissingProvenance(t *testing.T) {
@ -72,25 +66,18 @@ func TestInstallWithOptions_NoVerifyMissingProvenance(t *testing.T) {
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install with verification explicitly disabled should succeed without .prov
result, err := InstallWithOptions(installer, Options{Verify: false})
if err != nil {
t.Fatalf("Expected installation to succeed with --verify=false, got error: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when verification is disabled, got: %+v", result)
}
require.NoError(t, err, "Expected installation to succeed with --verify=false")
assert.Nil(t, result, "Expected nil verification result when verification is disabled, got: %+v", result)
// Plugin should be installed
if _, err := os.Stat(installer.Path()); os.IsNotExist(err) {
t.Errorf("Plugin should be installed at %s", installer.Path())
}
_, err = os.Stat(installer.Path())
assert.Falsef(t, os.IsNotExist(err), "Plugin should be installed at %s", installer.Path())
}
func TestInstallWithOptions_VerifyWithValidProvenance(t *testing.T) {
@ -110,9 +97,7 @@ func TestInstallWithOptions_VerifyWithValidProvenance(t *testing.T) {
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install with verification enabled
@ -120,20 +105,13 @@ func TestInstallWithOptions_VerifyWithValidProvenance(t *testing.T) {
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: keyring})
// Should fail due to invalid signature (empty keyring) but we test that it gets past the hash check
if err == nil {
t.Fatal("Expected installation to fail with empty keyring")
}
if !strings.Contains(err.Error(), "plugin verification failed") {
t.Errorf("Expected plugin verification failed error, got: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when verification fails, got: %+v", result)
}
require.Error(t, err, "Expected installation to fail with empty keyring")
require.ErrorContains(t, err, "plugin verification failed")
assert.Nil(t, result, "Expected nil verification result when verification fails, got: %+v", result)
// Plugin should not be installed due to verification failure
if _, err := os.Stat(installer.Path()); !os.IsNotExist(err) {
t.Error("Plugin should not be installed when verification fails")
}
_, err = os.Stat(installer.Path())
assert.True(t, os.IsNotExist(err), "Plugin should not be installed when verification fails")
}
func TestInstallWithOptions_VerifyWithInvalidProvenance(t *testing.T) {
@ -154,32 +132,23 @@ func TestInstallWithOptions_VerifyWithInvalidProvenance(t *testing.T) {
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install with verification enabled (should fail)
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: keyring})
// Should fail with verification error
if err == nil {
t.Fatal("Expected installation with invalid .prov file to fail")
}
if result != nil {
t.Errorf("Expected nil verification result when verification fails, got: %+v", result)
}
require.Error(t, err, "Expected installation with invalid .prov file to fail")
assert.Nil(t, result, "Expected nil verification result when verification fails, got: %+v", result)
// Should contain verification failure message
expectedError := "plugin verification failed"
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("Expected error message '%s', got: %s", expectedError, err.Error())
}
require.ErrorContains(t, err, expectedError)
// Plugin should not be installed
if _, err := os.Stat(installer.Path()); !os.IsNotExist(err) {
t.Error("Plugin should not be installed when verification fails")
}
_, err = os.Stat(installer.Path())
assert.True(t, os.IsNotExist(err), "Plugin should not be installed when verification fails")
}
func TestInstallWithOptions_NoVerifyRequested(t *testing.T) {
@ -192,26 +161,19 @@ func TestInstallWithOptions_NoVerifyRequested(t *testing.T) {
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install without verification (should succeed without any verification)
result, err := InstallWithOptions(installer, Options{Verify: false})
// Should succeed with no verification
if err != nil {
t.Fatalf("Expected installation without verification to succeed, got error: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when verification is disabled, got: %+v", result)
}
require.NoError(t, err, "Expected installation without verification to succeed")
assert.Nil(t, result, "Expected nil verification result when verification is disabled, got: %+v", result)
// Plugin should be installed
if _, err := os.Stat(installer.Path()); os.IsNotExist(err) {
t.Errorf("Plugin should be installed at %s", installer.Path())
}
_, err = os.Stat(installer.Path())
assert.Falsef(t, os.IsNotExist(err), "Plugin should be installed at %s", installer.Path())
}
func TestInstallWithOptions_VerifyDirectoryNotSupported(t *testing.T) {
@ -222,24 +184,16 @@ func TestInstallWithOptions_VerifyDirectoryNotSupported(t *testing.T) {
// Create local installer for directory
installer, err := NewLocalInstaller(pluginDir)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install with verification should fail (directories don't support verification)
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: "dummy"})
// Should fail with verification not supported error
if err == nil {
t.Fatal("Expected installation to fail with verification not supported error")
}
if !strings.Contains(err.Error(), "--verify is only supported for plugin tarballs") {
t.Errorf("Expected verification not supported error, got: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when verification fails, got: %+v", result)
}
require.Error(t, err, "Expected installation to fail with verification not supported error")
require.ErrorContains(t, err, "--verify is only supported for plugin tarballs")
assert.Nil(t, result, "Expected nil verification result when verification fails, got: %+v", result)
}
func TestInstallWithOptions_VerifyMismatchedProvenance(t *testing.T) {
@ -261,24 +215,16 @@ func TestInstallWithOptions_VerifyMismatchedProvenance(t *testing.T) {
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install with verification should fail due to hash mismatch
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: keyring})
// Should fail with verification error
if err == nil {
t.Fatal("Expected installation to fail with hash mismatch")
}
if !strings.Contains(err.Error(), "plugin verification failed") {
t.Errorf("Expected plugin verification failed error, got: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when verification fails, got: %+v", result)
}
require.Error(t, err, "Expected installation to fail with hash mismatch")
require.ErrorContains(t, err, "plugin verification failed")
assert.Nil(t, result, "Expected nil verification result when verification fails, got: %+v", result)
}
func TestInstallWithOptions_VerifyProvenanceAccessError(t *testing.T) {
@ -291,9 +237,7 @@ func TestInstallWithOptions_VerifyProvenanceAccessError(t *testing.T) {
// Create a .prov file but make it inaccessible (simulate permission error)
provFile := pluginTgz + ".prov"
if err := os.WriteFile(provFile, []byte("test"), 0o000); err != nil {
t.Fatalf("Failed to create inaccessible provenance file: %v", err)
}
require.NoErrorf(t, os.WriteFile(provFile, []byte("test"), 0o000), "Failed to create inaccessible provenance file")
defer os.Remove(provFile)
// Create keyring
@ -302,27 +246,21 @@ func TestInstallWithOptions_VerifyProvenanceAccessError(t *testing.T) {
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
require.NoError(t, err, "Failed to create installer")
defer os.RemoveAll(installer.Path())
// Install with verification should fail due to access error
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: keyring})
// Should fail with access error (either at stat level or during verification)
if err == nil {
t.Fatal("Expected installation to fail with provenance file access error")
}
require.Error(t, err, "Expected installation to fail with provenance file access error")
// The error could be either "failed to access provenance file" or "plugin verification failed"
// depending on when the permission error occurs
if !strings.Contains(err.Error(), "failed to access provenance file") &&
!strings.Contains(err.Error(), "plugin verification failed") {
t.Errorf("Expected provenance file access or verification error, got: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when verification fails, got: %+v", result)
}
assert.Nil(t, result, "Expected nil verification result when verification fails, got: %+v", result)
}
// Helper functions for test setup
@ -333,9 +271,7 @@ func createTestPluginDir(t *testing.T) string {
// Create temporary directory with plugin structure
tmpDir := t.TempDir()
pluginDir := filepath.Join(tmpDir, "test-plugin")
if err := os.MkdirAll(pluginDir, 0o755); err != nil {
t.Fatalf("Failed to create plugin directory: %v", err)
}
require.NoErrorf(t, os.MkdirAll(pluginDir, 0o755), "Failed to create plugin directory")
// Create plugin.yaml using the standardized v1 format
pluginYaml := `apiVersion: v1
@ -346,9 +282,7 @@ version: 1.0.0
runtimeConfig:
platformCommand:
- command: echo`
if err := os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(pluginYaml), 0o644); err != nil {
t.Fatalf("Failed to create plugin.yaml: %v", err)
}
require.NoErrorf(t, os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(pluginYaml), 0o644), "Failed to create plugin.yaml")
return pluginDir
}
@ -360,15 +294,10 @@ func createTarballFromPluginDir(t *testing.T, pluginDir string) string {
tmpDir := filepath.Dir(pluginDir)
tgzPath := filepath.Join(tmpDir, "test-plugin-1.0.0.tgz")
tarFile, err := os.Create(tgzPath)
if err != nil {
t.Fatalf("Failed to create tarball file: %v", err)
}
require.NoError(t, err, "Failed to create tarball file")
defer tarFile.Close()
if err := plugin.CreatePluginTarball(pluginDir, "test-plugin", tarFile); err != nil {
t.Fatalf("Failed to create tarball: %v", err)
}
require.NoErrorf(t, plugin.CreatePluginTarball(pluginDir, "test-plugin", tarFile), "Failed to create tarball")
return tgzPath
}
@ -379,9 +308,7 @@ func createProvFile(t *testing.T, provFile, pluginTgz, hash string) {
if hash == "" {
// Calculate actual hash of the tarball for realistic testing
data, err := os.ReadFile(pluginTgz)
if err != nil {
t.Fatalf("Failed to read tarball for hashing: %v", err)
}
require.NoError(t, err, "Failed to read tarball for hashing")
hashSum := sha256.Sum256(data)
hashStr = fmt.Sprintf("sha256:%x", hashSum)
} else {
@ -404,9 +331,7 @@ Version: GnuPG v1
iQEcBAEBCAAGBQJktest...
-----END PGP SIGNATURE-----
`, hashStr)
if err := os.WriteFile(provFile, []byte(provContent), 0o644); err != nil {
t.Fatalf("Failed to create provenance file: %v", err)
}
require.NoErrorf(t, os.WriteFile(provFile, []byte(provContent), 0o644), "Failed to create provenance file")
}
func createProvFileInvalidFormat(t *testing.T, provFile string) {
@ -414,9 +339,7 @@ func createProvFileInvalidFormat(t *testing.T, provFile string) {
// Create an invalid provenance file (not PGP signed format)
invalidProv := "This is not a valid PGP signed message"
if err := os.WriteFile(provFile, []byte(invalidProv), 0o644); err != nil {
t.Fatalf("Failed to create invalid provenance file: %v", err)
}
require.NoErrorf(t, os.WriteFile(provFile, []byte(invalidProv), 0o644), "Failed to create invalid provenance file")
}
func createTestKeyring(t *testing.T) string {
@ -427,9 +350,7 @@ func createTestKeyring(t *testing.T) string {
keyringPath := filepath.Join(tmpDir, "pubring.gpg")
// Create empty keyring for testing
if err := os.WriteFile(keyringPath, []byte{}, 0o644); err != nil {
t.Fatalf("Failed to create test keyring: %v", err)
}
require.NoErrorf(t, os.WriteFile(keyringPath, []byte{}, 0o644), "Failed to create test keyring")
return keyringPath
}

@ -194,13 +194,9 @@ func TestDetectDuplicates(t *testing.T) {
mockSubprocessCLIPlugin(t, "foo"),
mockSubprocessCLIPlugin(t, "bar"),
}
if err := detectDuplicates(plugs); err != nil {
t.Error("no duplicates in the first set")
}
require.NoError(t, detectDuplicates(plugs), "no duplicates in the first set")
plugs = append(plugs, mockSubprocessCLIPlugin(t, "foo"))
if err := detectDuplicates(plugs); err == nil {
t.Error("duplicates in the second set")
}
assert.Error(t, detectDuplicates(plugs), "duplicates in the second set")
}
func TestLoadAllDir_Empty(t *testing.T) {

@ -66,7 +66,7 @@ func TestValidatePluginData(t *testing.T) {
t.Run(strconv.Itoa(i), func(t *testing.T) {
err := item.plug.Metadata().Validate()
if item.pass {
require.NoError(t, err, "failed to validate case %d: %s", i, err)
require.NoError(t, err, "failed to validate case %d", i)
} else {
require.Error(t, err, "expected case %d to fail", i)
assert.ErrorContains(t, err, item.errString, "expected case %d error to contain %q", i, item.errString)
@ -97,8 +97,7 @@ func TestMetadataValidateVersion(t *testing.T) {
t.Run(name, func(t *testing.T) {
m := mockSubprocessCLIPlugin(t, "testplugin")
m.metadata.Version = tc.version
err := m.Metadata().Validate()
assert.NoError(t, err)
assert.NoError(t, m.Metadata().Validate())
})
}
@ -106,8 +105,7 @@ func TestMetadataValidateVersion(t *testing.T) {
t.Run(name, func(t *testing.T) {
m := mockSubprocessCLIPlugin(t, "testplugin")
m.metadata.Version = tc.version
err := m.Metadata().Validate()
assert.ErrorContains(t, err, "invalid plugin version")
assert.ErrorContains(t, m.Metadata().Validate(), "invalid plugin version")
})
}
}
@ -139,7 +137,7 @@ func TestMetadataValidateMultipleErrors(t *testing.T) {
}
for _, expectedErr := range expectedErrors {
assert.Contains(t, errStr, expectedErr, "expected error to contain %q, but got: %v", expectedErr, errStr)
require.ErrorContains(t, err, expectedErr)
}
// Verify that the error contains the correct number of error messages

@ -77,8 +77,7 @@ func TestMetadataV1ValidateVersion(t *testing.T) {
t.Run("invalid/"+name, func(t *testing.T) {
m := base()
m.Version = tc.version
err := m.Validate()
assert.ErrorContains(t, err, tc.errMsg)
assert.ErrorContains(t, m.Validate(), tc.errMsg)
})
}
}

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package plugin // import "helm.sh/helm/v4/internal/plugin"
package plugin
import (
"context"

@ -18,6 +18,8 @@ package plugin
import (
"testing"
"github.com/stretchr/testify/assert"
"helm.sh/helm/v4/internal/plugin/schema"
)
@ -34,9 +36,7 @@ func TestValidPluginName(t *testing.T) {
for name, pluginName := range validNames {
t.Run("valid/"+name, func(t *testing.T) {
if !validPluginName.MatchString(pluginName) {
t.Errorf("expected %q to match validPluginName regex", pluginName)
}
assert.Truef(t, validPluginName.MatchString(pluginName), "expected %q to match validPluginName regex", pluginName)
})
}
@ -52,9 +52,7 @@ func TestValidPluginName(t *testing.T) {
for name, pluginName := range invalidNames {
t.Run("invalid/"+name, func(t *testing.T) {
if validPluginName.MatchString(pluginName) {
t.Errorf("expected %q to not match validPluginName regex", pluginName)
}
assert.Falsef(t, validPluginName.MatchString(pluginName), "expected %q to not match validPluginName regex", pluginName)
})
}
}

@ -59,8 +59,7 @@ func buildLoadExtismPlugin(t *testing.T, dir string) pluginRaw {
func TestRuntimeConfigExtismV1Validate(t *testing.T) {
rc := RuntimeConfigExtismV1{}
err := rc.Validate()
assert.NoError(t, err, "expected no error for empty RuntimeConfigExtismV1")
assert.NoError(t, rc.Validate(), "expected no error for empty RuntimeConfigExtismV1")
}
func TestRuntimeExtismV1InvokePlugin(t *testing.T) {

@ -18,6 +18,7 @@ package plugin
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"log/slog"
@ -152,7 +153,8 @@ func (r *SubprocessPluginRuntime) InvokeHook(event string) error {
slog.Debug("executing plugin hook command", slog.String("pluginName", r.metadata.Name), slog.String("command", cmd.String()))
if err := cmd.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok {
var eerr *exec.ExitError
if errors.As(err, &eerr) {
os.Stderr.Write(eerr.Stderr)
return fmt.Errorf("plugin %s hook for %q exited with error", event, r.metadata.Name)
}
@ -166,7 +168,8 @@ func (r *SubprocessPluginRuntime) InvokeHook(event string) error {
// then replace the other three with a call to this func
func executeCmd(prog *exec.Cmd, pluginName string) error {
if err := prog.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok {
var eerr *exec.ExitError
if errors.As(err, &eerr) {
slog.Debug(
"plugin execution failed",
slog.String("pluginName", pluginName),

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package plugin // import "helm.sh/helm/v4/internal/plugin"
package plugin
// Types of hooks
const (

@ -16,7 +16,6 @@ limitations under the License.
package plugin
import (
"errors"
"fmt"
"os"
"path/filepath"
@ -78,8 +77,7 @@ func TestSubprocessPluginRuntime(t *testing.T) {
require.Error(t, err)
ieerr := &InvokeExecError{}
ok := errors.As(err, &ieerr)
require.True(t, ok, "expected InvokeExecError, got %T", err)
require.ErrorAs(t, err, &ieerr, "expected InvokeExecError")
assert.Equal(t, 56, ieerr.ExitCode)
assert.Nil(t, output)

@ -16,7 +16,6 @@ limitations under the License.
package plugin
import (
"reflect"
"runtime"
"testing"
@ -39,9 +38,7 @@ func TestPrepareCommand(t *testing.T) {
cmd, args, err := PrepareCommands(platformCommand, true, []string{}, env)
require.NoError(t, err)
require.Equal(t, cmdMain, cmd, "Expected %q, got %q", cmdMain, cmd)
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
require.Equalf(t, args, cmdArgs, "Expected %v, got %v", cmdArgs, args)
}
func TestPrepareCommandExtraArgs(t *testing.T) {
@ -113,9 +110,7 @@ func TestPrepareCommands(t *testing.T) {
cmd, args, err := PrepareCommands(cmds, true, []string{}, env)
require.NoError(t, err)
require.Equal(t, cmdMain, cmd, "Expected %q, got %q", cmdMain, cmd)
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
require.Equalf(t, args, cmdArgs, "Expected %v, got %v", cmdArgs, args)
}
func TestPrepareCommandsExtraArgs(t *testing.T) {
@ -136,9 +131,7 @@ func TestPrepareCommandsExtraArgs(t *testing.T) {
cmd, args, err := PrepareCommands(cmds, true, extraArgs, env)
require.NoError(t, err)
require.Equal(t, cmdMain, cmd, "Expected %q, got %q", cmdMain, cmd)
if !reflect.DeepEqual(args, expectedArgs) {
t.Fatalf("Expected %v, got %v", expectedArgs, args)
}
require.Equalf(t, expectedArgs, args, "Expected %v, got %v", expectedArgs, args)
}
func TestPrepareCommandsNoArch(t *testing.T) {
@ -155,9 +148,7 @@ func TestPrepareCommandsNoArch(t *testing.T) {
cmd, args, err := PrepareCommands(cmds, true, []string{}, env)
require.NoError(t, err)
require.Equal(t, cmdMain, cmd, "Expected %q, got %q", cmdMain, cmd)
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
require.Equalf(t, args, cmdArgs, "Expected %v, got %v", cmdArgs, args)
}
func TestPrepareCommandsNoOsNoArch(t *testing.T) {
@ -174,9 +165,7 @@ func TestPrepareCommandsNoOsNoArch(t *testing.T) {
cmd, args, err := PrepareCommands(cmds, true, []string{}, env)
require.NoError(t, err)
require.Equal(t, cmdMain, cmd, "Expected %q, got %q", cmdMain, cmd)
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
require.Equalf(t, args, cmdArgs, "Expected %v, got %v", cmdArgs, args)
}
func TestPrepareCommandsNoMatch(t *testing.T) {
@ -216,9 +205,7 @@ func TestPrepareCommandsExpand(t *testing.T) {
cmd, args, err := PrepareCommands(cmds, true, []string{}, env)
require.NoError(t, err)
require.Equal(t, cmdMain, cmd, "Expected %q, got %q", cmdMain, cmd)
if !reflect.DeepEqual(args, expectedArgs) {
t.Fatalf("Expected %v, got %v", expectedArgs, args)
}
require.Equalf(t, expectedArgs, args, "Expected %v, got %v", expectedArgs, args)
}
func TestPrepareCommandsNoExpand(t *testing.T) {
@ -235,7 +222,5 @@ func TestPrepareCommandsNoExpand(t *testing.T) {
cmd, args, err := PrepareCommands(cmds, false, []string{}, env)
require.NoError(t, err)
require.Equal(t, cmdMain, cmd, "Expected %q, got %q", cmdMain, cmd)
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
require.Equalf(t, args, cmdArgs, "Expected %v, got %v", cmdArgs, args)
}

@ -44,11 +44,9 @@ func TestVerifyPlugin(t *testing.T) {
// Create plugin directory
pluginDir := filepath.Join(tempDir, "verify-test-plugin")
err := os.MkdirAll(pluginDir, 0o755)
require.NoError(t, err)
require.NoError(t, os.MkdirAll(pluginDir, 0o755))
err = os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0o644)
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0o644))
// Create tarball
tarballPath := filepath.Join(tempDir, "verify-test-plugin.tar.gz")
@ -64,10 +62,9 @@ func TestVerifyPlugin(t *testing.T) {
// Sign the plugin with source directory
signer, err := provenance.NewFromKeyring(testKeyFile, "helm-test")
require.NoError(t, err)
err = signer.DecryptKey(func(_ string) ([]byte, error) {
require.NoError(t, signer.DecryptKey(func(_ string) ([]byte, error) {
return []byte(""), nil
})
require.NoError(t, err)
}))
// Read the tarball data
tarballData, err := os.ReadFile(tarballPath)
@ -78,8 +75,7 @@ func TestVerifyPlugin(t *testing.T) {
// Write the signature to .prov file
provFile := tarballPath + ".prov"
err = os.WriteFile(provFile, []byte(sig), 0o644)
require.NoError(t, err)
require.NoError(t, os.WriteFile(provFile, []byte(sig), 0o644))
// Read the files for verification
archiveData, err := os.ReadFile(tarballPath)
@ -105,11 +101,9 @@ func TestVerifyPluginBadSignature(t *testing.T) {
// Create a plugin tarball
pluginDir := filepath.Join(tempDir, "bad-plugin")
err := os.MkdirAll(pluginDir, 0o755)
require.NoError(t, err)
require.NoError(t, os.MkdirAll(pluginDir, 0o755))
err = os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0o644)
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0o644))
tarballPath := filepath.Join(tempDir, "bad-plugin.tar.gz")
tarFile, err := os.Create(tarballPath)
@ -133,8 +127,7 @@ InvalidSignatureData
-----END PGP SIGNATURE-----`
provFile := tarballPath + ".prov"
err = os.WriteFile(provFile, []byte(badSig), 0o644)
require.NoError(t, err)
require.NoError(t, os.WriteFile(provFile, []byte(badSig), 0o644))
// Read the files
archiveData, err := os.ReadFile(tarballPath)
@ -153,8 +146,7 @@ func TestVerifyPluginMissingProvenance(t *testing.T) {
tarballPath := filepath.Join(tempDir, "no-prov.tar.gz")
// Create a minimal tarball
err := os.WriteFile(tarballPath, []byte("dummy"), 0o644)
require.NoError(t, err)
require.NoError(t, os.WriteFile(tarballPath, []byte("dummy"), 0o644))
// Read the tarball data
archiveData, err := os.ReadFile(tarballPath)

@ -170,12 +170,12 @@ func TestHookExecutionUnmarshalJSON(t *testing.T) {
err := json.Unmarshal([]byte(tt.input), &exec)
if tt.wantErr {
assert.Error(t, err)
return
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected.StartedAt.Unix(), exec.StartedAt.Unix())
assert.Equal(t, tt.expected.CompletedAt.Unix(), exec.CompletedAt.Unix())
assert.Equal(t, tt.expected.Phase, exec.Phase)
}
require.NoError(t, err)
assert.Equal(t, tt.expected.StartedAt.Unix(), exec.StartedAt.Unix())
assert.Equal(t, tt.expected.CompletedAt.Unix(), exec.CompletedAt.Unix())
assert.Equal(t, tt.expected.Phase, exec.Phase)
})
}
}
@ -194,8 +194,7 @@ func TestHookExecutionRoundTrip(t *testing.T) {
require.NoError(t, err)
var decoded HookExecution
err = json.Unmarshal(data, &decoded)
require.NoError(t, err)
require.NoError(t, json.Unmarshal(data, &decoded))
assert.Equal(t, original.StartedAt.Unix(), decoded.StartedAt.Unix())
assert.Equal(t, original.CompletedAt.Unix(), decoded.CompletedAt.Unix())
@ -208,12 +207,11 @@ func TestHookExecutionEmptyStringRoundTrip(t *testing.T) {
input := `{"started_at":"","completed_at":"","phase":"Succeeded"}`
var exec HookExecution
err := json.Unmarshal([]byte(input), &exec)
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(input), &exec))
// Verify time fields are zero values
assert.True(t, exec.StartedAt.IsZero())
assert.True(t, exec.CompletedAt.IsZero())
assert.Zero(t, exec.StartedAt)
assert.Zero(t, exec.CompletedAt)
assert.Equal(t, HookPhaseSucceeded, exec.Phase)
// Marshal back and verify empty time fields are omitted
@ -221,8 +219,7 @@ func TestHookExecutionEmptyStringRoundTrip(t *testing.T) {
require.NoError(t, err)
var result map[string]any
err = json.Unmarshal(data, &result)
require.NoError(t, err)
require.NoError(t, json.Unmarshal(data, &result))
// Zero time values should be omitted
assert.NotContains(t, result, "started_at")

@ -253,17 +253,17 @@ func TestInfoUnmarshalJSON(t *testing.T) {
err := json.Unmarshal([]byte(tt.input), &info)
if tt.wantErr {
assert.Error(t, err)
return
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected.FirstDeployed.Unix(), info.FirstDeployed.Unix())
assert.Equal(t, tt.expected.LastDeployed.Unix(), info.LastDeployed.Unix())
assert.Equal(t, tt.expected.Deleted.Unix(), info.Deleted.Unix())
assert.Equal(t, tt.expected.Description, info.Description)
assert.Equal(t, tt.expected.Status, info.Status)
assert.Equal(t, tt.expected.RollbackRevision, info.RollbackRevision)
assert.Equal(t, tt.expected.Notes, info.Notes)
assert.Equal(t, tt.expected.Resources, info.Resources)
}
require.NoError(t, err)
assert.Equal(t, tt.expected.FirstDeployed.Unix(), info.FirstDeployed.Unix())
assert.Equal(t, tt.expected.LastDeployed.Unix(), info.LastDeployed.Unix())
assert.Equal(t, tt.expected.Deleted.Unix(), info.Deleted.Unix())
assert.Equal(t, tt.expected.Description, info.Description)
assert.Equal(t, tt.expected.Status, info.Status)
assert.Equal(t, tt.expected.RollbackRevision, info.RollbackRevision)
assert.Equal(t, tt.expected.Notes, info.Notes)
assert.Equal(t, tt.expected.Resources, info.Resources)
})
}
}
@ -284,9 +284,7 @@ func TestInfoRoundTrip(t *testing.T) {
require.NoError(t, err)
var decoded Info
err = json.Unmarshal(data, &decoded)
require.NoError(t, err)
require.NoError(t, json.Unmarshal(data, &decoded))
assert.Equal(t, original.FirstDeployed.Unix(), decoded.FirstDeployed.Unix())
assert.Equal(t, original.LastDeployed.Unix(), decoded.LastDeployed.Unix())
assert.Equal(t, original.Deleted.Unix(), decoded.Deleted.Unix())
@ -330,8 +328,7 @@ func TestInfoRollbackRevisionRoundTrip(t *testing.T) {
require.NoError(t, err)
var decoded Info
err = json.Unmarshal(data, &decoded)
require.NoError(t, err)
require.NoError(t, json.Unmarshal(data, &decoded))
assert.Equal(t, tt.info.RollbackRevision, decoded.RollbackRevision)
assert.Equal(t, tt.info.FirstDeployed.Unix(), decoded.FirstDeployed.Unix())
@ -342,8 +339,7 @@ func TestInfoRollbackRevisionRoundTrip(t *testing.T) {
// Verify omitempty behavior: zero rollback_revision should not appear in JSON
if tt.info.RollbackRevision == 0 {
var raw map[string]any
err = json.Unmarshal(data, &raw)
require.NoError(t, err)
require.NoError(t, json.Unmarshal(data, &raw))
assert.NotContains(t, raw, "rollback_revision")
}
})
@ -356,13 +352,12 @@ func TestInfoEmptyStringRoundTrip(t *testing.T) {
input := `{"first_deployed":"","last_deployed":"","deleted":"","status":"deployed","description":"test"}`
var info Info
err := json.Unmarshal([]byte(input), &info)
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(input), &info))
// Verify time fields are zero values
assert.True(t, info.FirstDeployed.IsZero())
assert.True(t, info.LastDeployed.IsZero())
assert.True(t, info.Deleted.IsZero())
assert.Zero(t, info.FirstDeployed)
assert.Zero(t, info.LastDeployed)
assert.Zero(t, info.Deleted)
assert.Equal(t, common.StatusDeployed, info.Status)
assert.Equal(t, "test", info.Description)
@ -371,8 +366,7 @@ func TestInfoEmptyStringRoundTrip(t *testing.T) {
require.NoError(t, err)
var result map[string]any
err = json.Unmarshal(data, &result)
require.NoError(t, err)
require.NoError(t, json.Unmarshal(data, &result))
// Zero time values should be omitted due to omitzero tag
assert.NotContains(t, result, "first_deployed")

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
v2 "helm.sh/helm/v4/internal/release/v2"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"testing"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"sort"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"bytes"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"fmt"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"fmt"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"testing"
@ -143,7 +143,6 @@ metadata:
// This test will fail if 'six' or 'seven' was added.
assert.Len(t, generic, 2)
assert.Len(t, hs, 4)
for _, out := range hs {

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"testing"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"sort"

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util // import "helm.sh/helm/v4/internal/release/v2/util"
package util
import (
"testing"

@ -148,28 +148,22 @@ func TestResolve(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l, err := r.Resolve(tt.req, repoNames)
if err != nil {
if tt.err {
return
}
if tt.err {
require.Error(t, err)
} else {
require.NoError(t, err)
}
h, err := HashReq(tt.req, tt.expect.Dependencies)
require.NoError(t, err)
assert.Equal(t, l.Digest, h, "%q: hashes don't match.", tt.name)
if tt.err {
require.Failf(t, "Expected error in test %q", tt.name)
// Check fields.
require.Len(t, l.Dependencies, len(tt.req), "%s: wrong number of dependencies in lock", tt.name)
d0 := l.Dependencies[0]
e0 := tt.expect.Dependencies[0]
assert.Equal(t, e0.Name, d0.Name, tt.name)
assert.Equal(t, e0.Repository, d0.Repository, tt.name)
assert.Equal(t, e0.Version, d0.Version, tt.name)
}
h, err := HashReq(tt.req, tt.expect.Dependencies)
require.NoError(t, err)
assert.Equal(t, l.Digest, h, "%q: hashes don't match.", tt.name)
// Check fields.
assert.Len(t, l.Dependencies, len(tt.req), "%s: wrong number of dependencies in lock", tt.name)
d0 := l.Dependencies[0]
e0 := tt.expect.Dependencies[0]
assert.Equal(t, e0.Name, d0.Name, tt.name)
assert.Equal(t, e0.Repository, d0.Repository, tt.name)
assert.Equal(t, e0.Version, d0.Version, tt.name)
})
}
}
@ -280,20 +274,16 @@ func TestGetLocalPath(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p, err := GetLocalPath(tt.repo, tt.chartpath)
if err != nil {
if tt.err {
return
}
require.NoError(t, err)
}
if tt.err {
require.Failf(t, "Expected error in test %q", tt.name)
}
expect := tt.expect
if runtime.GOOS == "windows" {
expect = tt.winExpect
require.Error(t, err)
} else {
require.NoError(t, err)
if runtime.GOOS == "windows" {
assert.Equal(t, tt.winExpect, p, tt.name)
} else {
assert.Equal(t, tt.expect, p, tt.name)
}
}
assert.Equal(t, expect, p, tt.name)
})
}
}

@ -21,6 +21,7 @@ limitations under the License.
package sympath
import (
"errors"
"fmt"
"log/slog"
"os"
@ -40,7 +41,7 @@ func Walk(root string, walkFn filepath.WalkFunc) error {
} else {
err = symwalk(root, info, walkFn)
}
if err == filepath.SkipDir {
if errors.Is(err, filepath.SkipDir) {
return nil
}
return err
@ -75,7 +76,7 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
if info, err = os.Lstat(resolved); err != nil {
return err
}
if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir {
if err := symwalk(path, info, walkFn); err != nil && !errors.Is(err, filepath.SkipDir) {
return err
}
return nil
@ -98,13 +99,13 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
filename := filepath.Join(path, name)
fileInfo, err := os.Lstat(filename)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
if err := walkFn(filename, fileInfo, err); err != nil && !errors.Is(err, filepath.SkipDir) {
return err
}
} else {
err = symwalk(filename, fileInfo, walkFn)
if err != nil {
if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir {
if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || !errors.Is(err, filepath.SkipDir) {
return err
}
}

@ -132,8 +132,7 @@ func TestWalk(t *testing.T) {
return mark(info, err, &errors, true)
}
// Expect no errors.
err := Walk(tree.name, markFn)
require.NoError(t, err)
require.NoError(t, Walk(tree.name, markFn))
require.Empty(t, errors, "unexpected errors")
checkMarks(t, true)

@ -32,54 +32,41 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package fs
import (
"errors"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRenameWithFallback(t *testing.T) {
dir := t.TempDir()
if err := RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil {
t.Fatal("expected an error for non existing file, but got nil")
}
require.Error(t, RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")), "expected an error for non existing file, but got nil")
srcpath := filepath.Join(dir, "src")
if srcf, err := os.Create(srcpath); err != nil {
t.Fatal(err)
} else {
srcf.Close()
}
srcf, err := os.Create(srcpath)
require.NoError(t, err)
srcf.Close()
if err := RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil {
t.Fatal(err)
}
require.NoError(t, RenameWithFallback(srcpath, filepath.Join(dir, "dst")))
srcpath = filepath.Join(dir, "a")
if err := os.MkdirAll(srcpath, 0o777); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(srcpath, 0o777))
dstpath := filepath.Join(dir, "b")
if err := os.MkdirAll(dstpath, 0o777); err != nil {
t.Fatal(err)
}
if err := RenameWithFallback(srcpath, dstpath); err == nil {
t.Fatal("expected an error if dst is an existing directory, but got nil")
}
require.NoError(t, os.MkdirAll(dstpath, 0o777))
require.Error(t, RenameWithFallback(srcpath, dstpath), "expected an error if dst is an existing directory, but got nil")
}
func TestCopyDir(t *testing.T) {
dir := t.TempDir()
srcdir := filepath.Join(dir, "src")
if err := os.MkdirAll(srcdir, 0o755); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(srcdir, 0o755))
files := []struct {
path string
@ -94,61 +81,40 @@ func TestCopyDir(t *testing.T) {
for i, file := range files {
fn := filepath.Join(srcdir, file.path)
dn := filepath.Dir(fn)
if err := os.MkdirAll(dn, 0o755); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(dn, 0o755))
fh, err := os.Create(fn)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if _, err = fh.WriteString(file.contents); err != nil {
t.Fatal(err)
}
_, err = fh.WriteString(file.contents)
require.NoError(t, err)
fh.Close()
files[i].fi, err = os.Stat(fn)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
}
destdir := filepath.Join(dir, "dest")
if err := CopyDir(srcdir, destdir); err != nil {
t.Fatal(err)
}
require.NoError(t, CopyDir(srcdir, destdir))
// Compare copy against structure indicated in 'files'
for _, file := range files {
fn := filepath.Join(srcdir, file.path)
dn := filepath.Dir(fn)
dirOK, err := IsDir(dn)
if err != nil {
t.Fatal(err)
}
if !dirOK {
t.Fatalf("expected %s to be a directory", dn)
}
require.NoError(t, err)
require.Truef(t, dirOK, "expected %s to be a directory", dn)
got, err := os.ReadFile(fn)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if file.contents != string(got) {
t.Fatalf("expected: %s, got: %s", file.contents, string(got))
}
require.Equalf(t, file.contents, string(got), "expected: %s, got: %s", file.contents, string(got))
gotinfo, err := os.Stat(fn)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if file.fi.Mode() != gotinfo.Mode() {
t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v",
file.path, file.fi.Mode(), fn, gotinfo.Mode())
}
require.Equalf(t, file.fi.Mode(), gotinfo.Mode(), "expected %s: %#v\n to be the same mode as %s: %#v",
file.path, file.fi.Mode(), fn, gotinfo.Mode())
}
}
@ -178,9 +144,7 @@ func TestCopyDirFail_SrcInaccessible(t *testing.T) {
dir := t.TempDir()
dstdir = filepath.Join(dir, "dst")
if err := CopyDir(srcdir, dstdir); err == nil {
t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
assert.Errorf(t, CopyDir(srcdir, dstdir), "expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
func TestCopyDirFail_DstInaccessible(t *testing.T) {
@ -203,9 +167,7 @@ func TestCopyDirFail_DstInaccessible(t *testing.T) {
dir := t.TempDir()
srcdir = filepath.Join(dir, "src")
if err := os.MkdirAll(srcdir, 0o755); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(srcdir, 0o755))
cleanup := setupInaccessibleDir(t, func(dir string) error {
dstdir = filepath.Join(dir, "dst")
@ -213,9 +175,7 @@ func TestCopyDirFail_DstInaccessible(t *testing.T) {
})
defer cleanup()
if err := CopyDir(srcdir, dstdir); err == nil {
t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
assert.Errorf(t, CopyDir(srcdir, dstdir), "expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
func TestCopyDirFail_SrcIsNotDir(t *testing.T) {
@ -225,44 +185,25 @@ func TestCopyDirFail_SrcIsNotDir(t *testing.T) {
dir := t.TempDir()
srcdir = filepath.Join(dir, "src")
if _, err = os.Create(srcdir); err != nil {
t.Fatal(err)
}
_, err = os.Create(srcdir)
require.NoError(t, err)
dstdir = filepath.Join(dir, "dst")
if err = CopyDir(srcdir, dstdir); err == nil {
t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
if !errors.Is(err, errSrcNotDir) {
t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err)
}
require.ErrorIsf(t, CopyDir(srcdir, dstdir), errSrcNotDir, "expected %v error for CopyDir(%s, %s)", errSrcNotDir, srcdir, dstdir)
}
func TestCopyDirFail_DstExists(t *testing.T) {
var srcdir, dstdir string
var err error
dir := t.TempDir()
srcdir = filepath.Join(dir, "src")
if err = os.MkdirAll(srcdir, 0o755); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(srcdir, 0o755))
dstdir = filepath.Join(dir, "dst")
if err = os.MkdirAll(dstdir, 0o755); err != nil {
t.Fatal(err)
}
if err = CopyDir(srcdir, dstdir); err == nil {
t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
if !errors.Is(err, errDstExist) {
t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err)
}
require.NoError(t, os.MkdirAll(dstdir, 0o755))
require.ErrorIs(t, CopyDir(srcdir, dstdir), errDstExist, "expected %v error for CopyDir(%s, %s)", errDstExist, srcdir, dstdir)
}
func TestCopyDirFailOpen(t *testing.T) {
@ -288,70 +229,46 @@ func TestCopyDirFailOpen(t *testing.T) {
dir := t.TempDir()
srcdir = filepath.Join(dir, "src")
if err := os.MkdirAll(srcdir, 0o755); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(srcdir, 0o755))
srcfn := filepath.Join(srcdir, "file")
srcf, err := os.Create(srcfn)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
srcf.Close()
// setup source file so that it cannot be read
if err = os.Chmod(srcfn, 0o222); err != nil {
t.Fatal(err)
}
require.NoError(t, os.Chmod(srcfn, 0o222))
dstdir = filepath.Join(dir, "dst")
if err = CopyDir(srcdir, dstdir); err == nil {
t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
assert.Errorf(t, CopyDir(srcdir, dstdir), "expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
}
func TestCopyFile(t *testing.T) {
dir := t.TempDir()
srcf, err := os.Create(filepath.Join(dir, "srcfile"))
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
want := "hello world"
if _, err := srcf.WriteString(want); err != nil {
t.Fatal(err)
}
_, err = srcf.WriteString(want)
require.NoError(t, err)
srcf.Close()
destf := filepath.Join(dir, "destf")
if err := CopyFile(srcf.Name(), destf); err != nil {
t.Fatal(err)
}
require.NoError(t, CopyFile(srcf.Name(), destf))
got, err := os.ReadFile(destf)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if want != string(got) {
t.Fatalf("expected: %s, got: %s", want, string(got))
}
require.Equalf(t, want, string(got), "expected: %s, got: %s", want, string(got))
wantinfo, err := os.Stat(srcf.Name())
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
gotinfo, err := os.Stat(destf)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if wantinfo.Mode() != gotinfo.Mode() {
t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode())
}
assert.Equalf(t, wantinfo.Mode(), gotinfo.Mode(), "expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode())
}
func TestCopyFileSymlink(t *testing.T) {
@ -366,9 +283,7 @@ func TestCopyFileSymlink(t *testing.T) {
for symlink, dst := range testcases {
t.Run(symlink, func(t *testing.T) {
var err error
if err = CopyFile(symlink, dst); err != nil {
t.Fatalf("failed to copy symlink: %s", err)
}
require.NoErrorf(t, CopyFile(symlink, dst), "failed to copy symlink")
var want, got string
@ -377,31 +292,21 @@ func TestCopyFileSymlink(t *testing.T) {
// regular users aren't granted usually. So we copy the file
// content as a fall back instead of creating a real symlink.
srcb, err := os.ReadFile(symlink)
if err != nil {
t.Fatalf("%+v", err)
}
require.NoError(t, err)
dstb, err := os.ReadFile(dst)
if err != nil {
t.Fatalf("%+v", err)
}
require.NoError(t, err)
want = string(srcb)
got = string(dstb)
} else {
want, err = os.Readlink(symlink)
if err != nil {
t.Fatalf("%+v", err)
}
require.NoError(t, err)
got, err = os.Readlink(dst)
if err != nil {
t.Fatalf("could not resolve symlink: %s", err)
}
require.NoErrorf(t, err, "could not resolve symlink")
}
if want != got {
t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got)
}
require.Equalf(t, want, got, "resolved path is incorrect. expected %s, got %s", want, got)
})
}
}
@ -424,9 +329,7 @@ func TestCopyFileFail(t *testing.T) {
dir := t.TempDir()
srcf, err := os.Create(filepath.Join(dir, "srcfile"))
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
srcf.Close()
var dstdir string
@ -438,9 +341,7 @@ func TestCopyFileFail(t *testing.T) {
defer cleanup()
fn := filepath.Join(dstdir, "file")
if err := CopyFile(srcf.Name(), fn); err == nil {
t.Fatalf("expected error for %s, got none", fn)
}
require.Errorf(t, CopyFile(srcf.Name(), fn), "expected error for %s, got none", fn)
}
// setupInaccessibleDir creates a temporary location with a single
@ -463,9 +364,7 @@ func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() {
subdir := filepath.Join(dir, "dir")
cleanup := func() {
if err := os.Chmod(subdir, 0o777); err != nil {
t.Error(err)
}
assert.NoError(t, os.Chmod(subdir, 0o777))
}
if err := os.Mkdir(subdir, 0o777); err != nil {
@ -498,9 +397,7 @@ func TestIsDir(t *testing.T) {
}
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
var dn string
@ -530,14 +427,16 @@ func TestIsDir(t *testing.T) {
}
for f, want := range tests {
got, err := IsDir(f)
if err != nil && !want.err {
t.Fatalf("expected no error, got %v", err)
}
t.Run(f, func(t *testing.T) {
got, err := IsDir(f)
if got != want.exists {
t.Fatalf("expected %t for %s, got %t", want.exists, f, got)
}
if want.err {
require.Error(t, err)
} else {
require.NoError(t, err)
}
require.Equalf(t, want.exists, got, "expected %t for %s, got %t", want.exists, f, got)
})
}
}
@ -552,26 +451,18 @@ func TestIsSymlink(t *testing.T) {
dir := t.TempDir()
dirPath := filepath.Join(dir, "directory")
if err := os.MkdirAll(dirPath, 0o777); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(dirPath, 0o777))
filePath := filepath.Join(dir, "file")
f, err := os.Create(filePath)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
f.Close()
dirSymlink := filepath.Join(dir, "dirSymlink")
fileSymlink := filepath.Join(dir, "fileSymlink")
if err = os.Symlink(dirPath, dirSymlink); err != nil {
t.Fatal(err)
}
if err = os.Symlink(filePath, fileSymlink); err != nil {
t.Fatal(err)
}
require.NoError(t, os.Symlink(dirPath, dirSymlink))
require.NoError(t, os.Symlink(filePath, fileSymlink))
var (
inaccessibleFile string
@ -609,14 +500,11 @@ func TestIsSymlink(t *testing.T) {
for path, want := range tests {
got, err := IsSymlink(path)
if err != nil {
if !want.err {
t.Errorf("expected no error, got %v", err)
}
}
if got != want.expected {
t.Errorf("expected %t for %s, got %t", want.expected, path, got)
if want.err {
require.Error(t, err, "expected an error")
} else {
require.NoError(t, err, "expected no error")
}
assert.Equalf(t, want.expected, got, "expected %t for %s, got %t", want.expected, path, got)
}
}

@ -58,9 +58,7 @@ func actionConfigFixtureWithDummyResources(t *testing.T, dummyResources kube.Res
slog.SetDefault(logger)
registryClient, err := registry.NewClient()
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
return &Configuration{
Releases: storage.Init(driver.NewMemory()),
@ -371,16 +369,10 @@ func TestGetVersionSet(t *testing.T) {
client := fakeclientset.NewClientset()
vs, err := GetVersionSet(client.Discovery())
if err != nil {
t.Error(err)
}
require.NoError(t, err)
if !vs.Has("v1") {
t.Error("Expected supported versions to at least include v1.")
}
if vs.Has("nosuchversion/v1") {
t.Error("Non-existent version is reported found.")
}
assert.True(t, vs.Has("v1"), "Expected supported versions to at least include v1.")
assert.False(t, vs.Has("nosuchversion/v1"), "Non-existent version is reported found.")
}
// Mock PostRenderer for testing
@ -2031,9 +2023,7 @@ spec:
mockPR := &mockPostRenderer{
transform: func(content string) string {
count := strings.Count(content, "kind: ServiceAccount")
if count > 1 {
t.Errorf("post-renderer received %d ServiceAccount resources in a single stream, expected at most 1", count)
}
assert.LessOrEqualf(t, count, 1, "post-renderer received %d ServiceAccount resources in a single stream, expected at most 1", count)
return content
},
}

@ -57,9 +57,7 @@ func TestList(t *testing.T) {
},
} {
buf := bytes.Buffer{}
if err := NewDependency().List(tcase.chart, &buf); err != nil {
t.Fatal(err)
}
require.NoError(t, NewDependency().List(tcase.chart, &buf))
test.AssertGoldenString(t, buf.String(), tcase.golden)
}
}
@ -71,22 +69,16 @@ func TestDependencyStatus_Dashes(t *testing.T) {
dir := t.TempDir()
chartpath := filepath.Join(dir, "charts")
if err := os.MkdirAll(chartpath, 0o700); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(chartpath, 0o700))
// Add some fake charts
first := buildChart(withName("first-chart"))
_, err := chartutil.Save(first, chartpath)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
second := buildChart(withName("first-chart-second-chart"))
_, err = chartutil.Save(second, chartpath)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
dep := &chart.Dependency{
Name: "first-chart",
@ -95,9 +87,7 @@ func TestDependencyStatus_Dashes(t *testing.T) {
// Now try to get the deps
stat := NewDependency().dependencyStatus(dir, dep, first)
if stat != "ok" {
t.Errorf("Unexpected status: %q", stat)
}
assert.Equal(t, "ok", stat, "Unexpected status: %q", stat)
}
func TestStatArchiveForStatus(t *testing.T) {
@ -105,9 +95,7 @@ func TestStatArchiveForStatus(t *testing.T) {
dir := t.TempDir()
chartpath := filepath.Join(dir, "charts")
if err := os.MkdirAll(chartpath, 0o700); err != nil {
t.Fatal(err)
}
require.NoError(t, os.MkdirAll(chartpath, 0o700))
// unsaved chart
lilith := buildChart(withName("lilith"))

@ -65,8 +65,7 @@ func TestGetMetadata_Run_BasicMetadata(t *testing.T) {
Namespace: "default",
}
err := cfg.Releases.Create(rel)
require.NoError(t, err)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -645,8 +644,7 @@ func TestGetMetadata_Labels(t *testing.T) {
rel.Labels = customLabels
metaGetter := NewGetMetadata(actionConfigFixture(t))
err := metaGetter.cfg.Releases.Create(rel)
require.NoError(t, err)
require.NoError(t, metaGetter.cfg.Releases.Create(rel))
metadata, err := metaGetter.Run(rel.Name)
require.NoError(t, err)

@ -50,9 +50,7 @@ func TestHistoryRun(t *testing.T) {
client.Max = 3
client.cfg.Releases.MaxHistory = 3
for _, rel := range []*release.Release{simpleRelease, updatedRelease} {
if err := client.cfg.Releases.Create(rel); err != nil {
t.Fatal(err, "Could not add releases to Config")
}
require.NoError(t, client.cfg.Releases.Create(rel), "Could not add releases to Config")
}
releases, err := config.Releases.ListReleases()

@ -401,16 +401,12 @@ data:
serverSideApply := true
err := configuration.execHook(&tc.inputRelease, hookEvent, kube.StatusWatcherStrategy, nil, 600, serverSideApply)
if !reflect.DeepEqual(kubeClient.deleteRecord, tc.expectedDeleteRecord) {
t.Fatalf("Got unexpected delete record, expected: %#v, but got: %#v", kubeClient.deleteRecord, tc.expectedDeleteRecord)
}
if err != nil && !tc.expectError {
t.Fatal("Got an unexpected error.")
}
require.Truef(t, reflect.DeepEqual(kubeClient.deleteRecord, tc.expectedDeleteRecord), "Got unexpected delete record, expected: %#v, but got: %#v", kubeClient.deleteRecord, tc.expectedDeleteRecord)
if err == nil && tc.expectError {
t.Fatal("Expected and error but did not get it.")
if !tc.expectError {
require.NoError(t, err)
} else {
require.Error(t, err)
}
})
}
@ -490,8 +486,7 @@ data:
ctx := context.Background()
waitOptions := []kube.WaitOption{kube.WithWaitContext(ctx)}
err := configuration.execHook(rel, release.HookPreInstall, kube.StatusWatcherStrategy, waitOptions, 600, false)
req.NoError(err)
req.NoError(configuration.execHook(rel, release.HookPreInstall, kube.StatusWatcherStrategy, waitOptions, 600, false))
// Verify that WaitOptions were passed to GetWaiter
is.NotEmpty(failer.RecordedWaitOptions, "WaitOptions should be passed to GetWaiter")

@ -185,9 +185,7 @@ func TestInstallRelease(t *testing.T) {
vals := map[string]any{}
ctx, done := context.WithCancel(t.Context())
resi, err := instAction.RunWithContext(ctx, buildChart(), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
is.Equal("test-install-release", res.Name, "Expected release name.")
@ -238,9 +236,7 @@ func TestInstallReleaseWithTakeOwnership_ResourceNotOwned(t *testing.T) {
instAction := installActionWithConfig(config)
instAction.TakeOwnership = true
resi, err := instAction.Run(buildChart(), nil)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -262,9 +258,7 @@ func TestInstallReleaseWithTakeOwnership_ResourceOwned(t *testing.T) {
instAction := installActionWithConfig(config)
instAction.TakeOwnership = false
resi, err := instAction.Run(buildChart(), nil)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
r, err := instAction.cfg.Releases.Get(res.Name, res.Version)
@ -285,7 +279,7 @@ func TestInstallReleaseWithTakeOwnership_ResourceOwnedNoFlag(t *testing.T) {
instAction := installActionWithConfig(config)
_, err := instAction.Run(buildChart(), nil)
req.Error(err)
is.Contains(err.Error(), "unable to continue with install")
is.ErrorContains(err, "unable to continue with install")
}
func TestInstallReleaseWithValues(t *testing.T) {
@ -303,9 +297,7 @@ func TestInstallReleaseWithValues(t *testing.T) {
},
}
resi, err := instAction.Run(buildChart(withSampleValues()), userVals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
is.Equal("test-install-release", res.Name, "Expected release name.")
@ -344,9 +336,7 @@ func TestInstallRelease_WithNotes(t *testing.T) {
instAction.ReleaseName = "with-notes"
vals := map[string]any{}
resi, err := instAction.Run(buildChart(withNotes("note here")), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -376,9 +366,7 @@ func TestInstallRelease_WithNotesRendered(t *testing.T) {
instAction.ReleaseName = "with-notes"
vals := map[string]any{}
resi, err := instAction.Run(buildChart(withNotes("got-{{.Release.Name}}")), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -400,9 +388,7 @@ func TestInstallRelease_WithChartAndDependencyParentNotes(t *testing.T) {
instAction.ReleaseName = "with-notes"
vals := map[string]any{}
resi, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -424,9 +410,7 @@ func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) {
instAction.SubNotes = true
vals := map[string]any{}
resi, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -436,9 +420,7 @@ func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) {
req.NoError(err)
is.Equal("with-notes", rel.Name)
// test run can return as either 'parent\nchild' or 'child\nparent'
if !strings.Contains(rel.Info.Notes, "parent") && !strings.Contains(rel.Info.Notes, "child") {
t.Fatalf("Expected 'parent\nchild' or 'child\nparent', got '%s'", rel.Info.Notes)
}
req.True(strings.Contains(rel.Info.Notes, "parent") || strings.Contains(rel.Info.Notes, "child"), "Expected 'parent\nchild' or 'child\nparent', got '%s'", rel.Info.Notes)
is.Equal("Install complete", rel.Info.Description)
}
@ -451,9 +433,7 @@ func TestInstallRelease_DryRunClient(t *testing.T) {
vals := map[string]any{}
resi, err := instAction.Run(buildChart(withSampleTemplates()), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -466,7 +446,7 @@ func TestInstallRelease_DryRunClient(t *testing.T) {
_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
req.Error(err)
is.Len(res.Hooks, 1)
is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "expect hook to not be marked as run")
is.Zero(res.Hooks[0].LastRun.CompletedAt, "expect hook to not be marked as run")
is.Equal("Dry run complete", res.Info.Description)
}
}
@ -480,9 +460,7 @@ func TestInstallRelease_DryRunHiddenSecret(t *testing.T) {
instAction.DryRunStrategy = DryRunClient
vals := map[string]any{}
resi, err := instAction.Run(buildChart(withSampleSecret(), withSampleTemplates()), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
is.Contains(res.Manifest, "---\n# Source: hello/templates/secret.yaml\napiVersion: v1\nkind: Secret")
@ -495,9 +473,7 @@ func TestInstallRelease_DryRunHiddenSecret(t *testing.T) {
instAction.HideSecret = true
vals = map[string]any{}
res2i, err := instAction.Run(buildChart(withSampleSecret(), withSampleTemplates()), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res2, err := releaserToV1Release(res2i)
req.NoError(err)
@ -511,9 +487,7 @@ func TestInstallRelease_DryRunHiddenSecret(t *testing.T) {
instAction.DryRunStrategy = DryRunNone
vals = map[string]any{}
_, err = instAction.Run(buildChart(withSampleSecret(), withSampleTemplates()), vals)
if err == nil {
t.Fatal("Did not get the expected error when dry-run is false and hide secret is true")
}
req.Error(err, "Did not get the expected error when dry-run is false and hide secret is true")
}
// Regression test for #7955
@ -532,9 +506,7 @@ func TestInstallRelease_DryRun_Lookup(t *testing.T) {
})
resi, err := instAction.Run(mockChart, vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -543,6 +515,7 @@ func TestInstallRelease_DryRun_Lookup(t *testing.T) {
func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) {
is := assert.New(t)
req := require.New(t)
instAction := installAction(t)
instAction.DryRunStrategy = DryRunNone
vals := map[string]any{}
@ -550,10 +523,8 @@ func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) {
expectedErr := `hello/templates/incorrect:1:10
executing "hello/templates/incorrect" at <.Values.bad.doh>:
nil pointer evaluating interface {}.doh`
if err == nil {
t.Fatalf("Install should fail containing error: %s", expectedErr)
}
is.Contains(err.Error(), expectedErr)
req.Error(err, "Install should fail containing error: %s", expectedErr)
is.ErrorContains(err, expectedErr)
}
func TestInstallRelease_NoHooks(t *testing.T) {
@ -562,17 +533,15 @@ func TestInstallRelease_NoHooks(t *testing.T) {
instAction := installAction(t)
instAction.DisableHooks = true
instAction.ReleaseName = "no-hooks"
require.NoError(t, instAction.cfg.Releases.Create(releaseStub()))
req.NoError(instAction.cfg.Releases.Create(releaseStub()))
vals := map[string]any{}
resi, err := instAction.Run(buildChart(), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "hooks should not run with no-hooks")
is.Zero(res.Hooks[0].LastRun.CompletedAt, "hooks should not run with no-hooks")
}
func TestInstallRelease_FailedHooks(t *testing.T) {
@ -637,7 +606,7 @@ func TestInstallRelease_KubeVersion(t *testing.T) {
vals = map[string]any{}
_, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals)
req.Error(err)
is.Contains(err.Error(), "chart requires kubeVersion: >=99.0.0 which is incompatible with Kubernetes v1.20.")
is.ErrorContains(err, "chart requires kubeVersion: >=99.0.0 which is incompatible with Kubernetes v1.20.")
}
func TestInstallRelease_Wait(t *testing.T) {
@ -680,7 +649,7 @@ func TestInstallRelease_Wait_Interrupted(t *testing.T) {
_, err := instAction.RunWithContext(ctx, buildChart(), vals)
req.Error(err)
is.Contains(err.Error(), "context canceled")
req.ErrorContains(err, "context canceled")
is.Equal(goroutines+1, instAction.getGoroutineCount()) // installation goroutine still is in background
time.Sleep(10 * time.Second) // wait for goroutine to finish
@ -723,8 +692,8 @@ func TestInstallRelease_RollbackOnFailure(t *testing.T) {
resi, err := instAction.Run(buildChart(), vals)
req.Error(err)
is.Contains(err.Error(), "I timed out")
is.Contains(err.Error(), "rollback-on-failure")
req.ErrorContains(err, "I timed out")
req.ErrorContains(err, "rollback-on-failure")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -748,9 +717,9 @@ func TestInstallRelease_RollbackOnFailure(t *testing.T) {
_, err := instAction.Run(buildChart(), vals)
req.Error(err)
is.Contains(err.Error(), "I timed out")
is.Contains(err.Error(), "uninstall fail")
is.Contains(err.Error(), "an error occurred while uninstalling the release")
req.ErrorContains(err, "I timed out")
req.ErrorContains(err, "uninstall fail")
is.ErrorContains(err, "an error occurred while uninstalling the release")
})
}
func TestInstallRelease_RollbackOnFailure_Interrupted(t *testing.T) {
@ -771,9 +740,9 @@ func TestInstallRelease_RollbackOnFailure_Interrupted(t *testing.T) {
resi, err := instAction.RunWithContext(ctx, buildChart(), vals)
req.Error(err)
is.Contains(err.Error(), "context canceled")
is.Contains(err.Error(), "rollback-on-failure")
is.Contains(err.Error(), "uninstalled")
req.ErrorContains(err, "context canceled")
req.ErrorContains(err, "rollback-on-failure")
req.ErrorContains(err, "uninstalled")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -820,36 +789,23 @@ func TestNameTemplate(t *testing.T) {
}
for _, tc := range testCases {
n, err := TemplateName(tc.tpl)
if err != nil {
t.Run(tc.tpl, func(t *testing.T) {
n, err := TemplateName(tc.tpl)
if tc.expectedErrorStr == "" {
t.Errorf("Was not expecting error, but got: %v", err)
continue
}
re, compErr := regexp.Compile(tc.expectedErrorStr)
if compErr != nil {
t.Errorf("Expected error string failed to compile: %v", compErr)
continue
}
if !re.MatchString(err.Error()) {
t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err)
continue
require.NoError(t, err)
} else {
require.Error(t, err)
re, compErr := regexp.Compile(tc.expectedErrorStr)
require.NoError(t, compErr, "Expected error string failed to compile")
assert.True(t, re.MatchString(err.Error()), "Error didn't match for %s expected %s", tc.tpl, tc.expectedErrorStr)
}
}
if err == nil && tc.expectedErrorStr != "" {
t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr)
}
if tc.expected != "" {
re, err := regexp.Compile(tc.expected)
if err != nil {
t.Errorf("Expected string failed to compile: %v", err)
continue
if tc.expected != "" {
re, err := regexp.Compile(tc.expected)
require.NoError(t, err)
assert.True(t, re.MatchString(n), "Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n)
}
if !re.MatchString(n) {
t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n)
}
}
})
}
}
@ -864,9 +820,7 @@ func TestInstallReleaseOutputDir(t *testing.T) {
instAction.OutputDir = dir
_, err := instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
_, err = os.Stat(filepath.Join(dir, "hello", "templates", "goodbye"))
req.NoError(err)
@ -901,9 +855,7 @@ func TestInstallOutputDirWithReleaseName(t *testing.T) {
newDir := filepath.Join(dir, instAction.ReleaseName)
_, err := instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
_, err = os.Stat(filepath.Join(newDir, "hello", "templates", "goodbye"))
req.NoError(err)
@ -925,46 +877,37 @@ func TestInstallOutputDirWithReleaseName(t *testing.T) {
func TestNameAndChart(t *testing.T) {
is := assert.New(t)
req := require.New(t)
instAction := installAction(t)
chartName := "./foo"
name, chrt, err := instAction.NameAndChart([]string{chartName})
if err != nil {
t.Fatal(err)
}
req.NoError(err)
is.Equal(instAction.ReleaseName, name)
is.Equal(chartName, chrt)
instAction.GenerateName = true
_, _, err = instAction.NameAndChart([]string{"foo", chartName})
if err == nil {
t.Fatal("expected an error")
}
is.Equal("cannot set --generate-name and also specify a name", err.Error())
req.Error(err, "expected an error")
req.EqualError(err, "cannot set --generate-name and also specify a name")
instAction.GenerateName = false
instAction.NameTemplate = "{{ . }}"
_, _, err = instAction.NameAndChart([]string{"foo", chartName})
if err == nil {
t.Fatal("expected an error")
}
is.Equal("cannot set --name-template and also specify a name", err.Error())
req.Error(err, "expected an error")
req.EqualError(err, "cannot set --name-template and also specify a name")
instAction.NameTemplate = ""
instAction.ReleaseName = ""
_, _, err = instAction.NameAndChart([]string{chartName})
if err == nil {
t.Fatal("expected an error")
}
is.Equal("must either provide a name or specify --generate-name", err.Error())
req.Error(err, "expected an error")
req.EqualError(err, "must either provide a name or specify --generate-name")
instAction.NameTemplate = ""
instAction.ReleaseName = ""
_, _, err = instAction.NameAndChart([]string{"foo", chartName, "bar"})
if err == nil {
t.Fatal("expected an error")
}
is.Equal("expected at most two arguments, unexpected arguments: bar", err.Error())
req.Error(err, "expected an error")
is.EqualError(err, "expected at most two arguments, unexpected arguments: bar")
}
func TestNameAndChartGenerateName(t *testing.T) {
@ -1016,9 +959,7 @@ func TestNameAndChartGenerateName(t *testing.T) {
t.Parallel()
name, chrt, err := instAction.NameAndChart([]string{tc.Chart})
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
is.Equal(tc.ExpectedName, name)
is.Equal(tc.Chart, chrt)
@ -1035,9 +976,7 @@ func TestInstallWithLabels(t *testing.T) {
"key2": "val2",
}
resi, err := instAction.Run(buildChart(), nil)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
req.NoError(err, "Failed install")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -1046,17 +985,15 @@ func TestInstallWithLabels(t *testing.T) {
func TestInstallWithSystemLabels(t *testing.T) {
is := assert.New(t)
req := require.New(t)
instAction := installAction(t)
instAction.Labels = map[string]string{
"owner": "val1",
"key2": "val2",
}
_, err := instAction.Run(buildChart(), nil)
if err == nil {
t.Fatal("expected an error")
}
is.Equal(fmt.Errorf("user supplied labels contains system reserved label name. System labels: %+v", driver.GetSystemLabels()), err)
req.Error(err, "expected an error")
is.EqualError(err, fmt.Sprintf("user supplied labels contains system reserved label name. System labels: %+v", driver.GetSystemLabels()))
}
func TestUrlEqual(t *testing.T) {
@ -1111,13 +1048,9 @@ func TestUrlEqual(t *testing.T) {
t.Parallel()
u1, err := url.Parse(tc.url1)
if err != nil {
t.Fatalf("Failed to parse URL1 %s: %v", tc.url1, err)
}
require.NoError(t, err, "Failed to parse URL1 %s", tc.url1)
u2, err := url.Parse(tc.url2)
if err != nil {
t.Fatalf("Failed to parse URL2 %s: %v", tc.url2, err)
}
require.NoError(t, err, "Failed to parse URL2 %s", tc.url2)
is.Equal(tc.expected, urlEqual(u1, u2))
})
@ -1161,7 +1094,7 @@ func TestInstallCRDs(t *testing.T) {
mockChart := buildChart(withFile(mockFile))
crdsToInstall := mockChart.CRDObjects()
assert.Len(t, crdsToInstall, 1)
require.Len(t, crdsToInstall, 1)
assert.Equal(t, crdsToInstall[0].File.Data, mockFile.Data)
require.NoError(t, instAction.installCRDs(crdsToInstall))
}
@ -1285,11 +1218,7 @@ func TestInstallCRDs_CheckNilErrors(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
instAction := installAction(t)
err := instAction.installCRDs(tt.input)
if err == nil {
t.Error("got nil expected err")
}
assert.Error(t, instAction.installCRDs(tt.input), "got nil expected err")
})
}
}

@ -21,6 +21,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/chart/v2/lint/support"
)
@ -89,11 +90,10 @@ func TestLintChart(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := lintChart(tt.chartPath, map[string]any{}, namespace, nil, tt.skipSchemaValidation)
switch {
case err != nil && !tt.err:
t.Errorf("%s", err)
case err == nil && tt.err:
t.Error("Expected a chart parsing error")
if tt.err {
require.Error(t, err, "Expected a chart parsing error")
} else {
require.NoError(t, err)
}
})
}
@ -106,14 +106,8 @@ func TestNonExistentChart(t *testing.T) {
testLint := NewLint()
result := testLint.Run(testCharts, values)
if len(result.Errors) != 1 {
t.Error("expected one error, but got", len(result.Errors))
}
actual := result.Errors[0].Error()
if actual != expectedError {
t.Errorf("expected '%s', but got '%s'", expectedError, actual)
}
require.Len(t, result.Errors, 1, "expected one error, but got", len(result.Errors))
assert.EqualError(t, result.Errors[0], expectedError)
})
t.Run("should error out for corrupted tgz chart", func(t *testing.T) {
@ -122,31 +116,23 @@ func TestNonExistentChart(t *testing.T) {
testLint := NewLint()
result := testLint.Run(testCharts, values)
if len(result.Errors) != 1 {
t.Error("expected one error, but got", len(result.Errors))
}
actual := result.Errors[0].Error()
if actual != expectedEOFError {
t.Errorf("expected '%s', but got '%s'", expectedEOFError, actual)
}
require.Len(t, result.Errors, 1, "expected one error, but got", len(result.Errors))
assert.EqualError(t, result.Errors[0], expectedEOFError)
})
}
func TestLint_MultipleCharts(t *testing.T) {
testCharts := []string{chart2MultipleChartLint, chart1MultipleChartLint}
testLint := NewLint()
if result := testLint.Run(testCharts, values); len(result.Errors) > 0 {
t.Error(result.Errors)
}
result := testLint.Run(testCharts, values)
assert.Empty(t, result.Errors)
}
func TestLint_EmptyResultErrors(t *testing.T) {
testCharts := []string{chart2MultipleChartLint}
testLint := NewLint()
if result := testLint.Run(testCharts, values); len(result.Errors) > 0 {
t.Error("Expected no error, got more")
}
result := testLint.Run(testCharts, values)
assert.Empty(t, result.Errors, "Expected no error, got more")
}
func TestLint_ChartWithWarnings(t *testing.T) {
@ -154,18 +140,16 @@ func TestLint_ChartWithWarnings(t *testing.T) {
testCharts := []string{chartWithNoTemplatesDir}
testLint := NewLint()
testLint.Strict = false
if result := testLint.Run(testCharts, values); len(result.Errors) > 0 {
t.Error("Expected no error, got more")
}
result := testLint.Run(testCharts, values)
assert.Empty(t, result.Errors, "Expected no error, got more")
})
t.Run("should fail with one error when strict", func(t *testing.T) {
testCharts := []string{chartWithNoTemplatesDir}
testLint := NewLint()
testLint.Strict = true
if result := testLint.Run(testCharts, values); len(result.Errors) != 1 {
t.Error("expected one error, but got", len(result.Errors))
}
result := testLint.Run(testCharts, values)
assert.Len(t, result.Errors, 1, "expected one error")
})
}

@ -44,9 +44,7 @@ func TestListStates(t *testing.T) {
"unknown": ListUnknown,
"totally made up key": ListUnknown,
} {
if expect != expect.FromName(input) {
t.Errorf("Expected %d for %s", expect, input)
}
assert.Equal(t, expect.FromName(input), expect, "Expected %d for %s", expect, input)
// This is a cheap way to verify that ListAll actually allows everything but Unknown
if got := expect.FromName(input); got != ListUnknown && got&ListAll == 0 {
t.Errorf("Expected %s to match the ListAll filter", input)
@ -54,12 +52,10 @@ func TestListStates(t *testing.T) {
}
filter := ListDeployed | ListPendingRollback
if status := filter.FromName("deployed"); filter&status == 0 {
t.Errorf("Expected %d to match mask %d", status, filter)
}
if status := filter.FromName("failed"); filter&status != 0 {
t.Errorf("Expected %d to fail to match mask %d", status, filter)
}
status := filter.FromName("deployed")
assert.NotEqualf(t, ListStates(0), filter&status, "Expected %d to match mask %d", status, filter)
status = filter.FromName("failed")
assert.Equalf(t, ListStates(0), filter&status, "Expected %d to fail to match mask %d", status, filter)
}
func TestList_Empty(t *testing.T) {
@ -202,8 +198,7 @@ func TestList_StateMask(t *testing.T) {
}
one.SetStatus(common.StatusUninstalled, "uninstalled")
err = lister.cfg.Releases.Update(one)
req.NoError(err)
req.NoError(lister.cfg.Releases.Update(one))
res, err := lister.Run()
req.NoError(err)
@ -277,9 +272,7 @@ func makeMeSomeReleasesWithStaleFailure(t *testing.T, store *storage.Storage) {
five.Version = 1
for _, rel := range []*release.Release{one, two, three, four, five} {
if err := store.Create(rel); err != nil {
t.Fatal(err)
}
require.NoError(t, store.Create(rel))
}
all, err := store.ListReleases()
@ -328,9 +321,7 @@ func makeMeSomeReleases(t *testing.T, store *storage.Storage) {
three.Version = 3
for _, rel := range []*release.Release{one, two, three} {
if err := store.Create(rel); err != nil {
t.Fatal(err)
}
require.NoError(t, store.Create(rel))
}
all, err := store.ListReleases()
@ -389,9 +380,7 @@ func TestSelectorList(t *testing.T) {
lister := newListFixture(t)
for _, rel := range []*release.Release{r1, r2, r3} {
if err := lister.cfg.Releases.Create(rel); err != nil {
t.Fatal(err)
}
require.NoError(t, lister.cfg.Releases.Create(rel))
}
t.Run("should fail selector parsing", func(t *testing.T) {

@ -17,7 +17,6 @@ limitations under the License.
package action
import (
"errors"
"os"
"path"
"testing"
@ -35,18 +34,12 @@ func TestPassphraseFileFetcher(t *testing.T) {
testPkg := NewPackage()
fetcher, err := testPkg.passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
if err != nil {
t.Fatal("Unable to create passphraseFileFetcher", err)
}
require.NoError(t, err, "Unable to create passphraseFileFetcher")
passphrase, err := fetcher("key")
if err != nil {
t.Fatal("Unable to fetch passphrase")
}
require.NoError(t, err, "Unable to fetch passphrase")
if string(passphrase) != secret {
t.Errorf("Expected %s got %s", secret, string(passphrase))
}
assert.Equal(t, secret, string(passphrase), "Expected %s got %s", secret, string(passphrase))
}
func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) {
@ -55,18 +48,12 @@ func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) {
testPkg := NewPackage()
fetcher, err := testPkg.passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
if err != nil {
t.Fatal("Unable to create passphraseFileFetcher", err)
}
require.NoError(t, err, "Unable to create passphraseFileFetcher")
passphrase, err := fetcher("key")
if err != nil {
t.Fatal("Unable to fetch passphrase")
}
require.NoError(t, err, "Unable to fetch passphrase")
if string(passphrase) != secret {
t.Errorf("Expected %s got %s", secret, string(passphrase))
}
assert.Equal(t, secret, string(passphrase), "Expected %s got %s", secret, string(passphrase))
}
func TestPassphraseFileFetcher_WithInvalidStdin(t *testing.T) {
@ -74,21 +61,16 @@ func TestPassphraseFileFetcher_WithInvalidStdin(t *testing.T) {
testPkg := NewPackage()
stdin, err := os.CreateTemp(directory, "non-existing")
if err != nil {
t.Fatal("Unable to create test file", err)
}
require.NoError(t, err, "Unable to create test file")
if _, err := testPkg.passphraseFileFetcher("-", stdin); err == nil {
t.Error("Expected passphraseFileFetcher returning an error")
}
_, err = testPkg.passphraseFileFetcher("-", stdin)
assert.Error(t, err, "Expected passphraseFileFetcher returning an error")
}
func TestPassphraseFileFetcher_WithStdinAndMultipleFetches(t *testing.T) {
testPkg := NewPackage()
stdin, w, err := os.Pipe()
if err != nil {
t.Fatal("Unable to create pipe", err)
}
require.NoError(t, err, "Unable to create pipe")
passphrase := "secret-from-stdin"
@ -99,18 +81,12 @@ func TestPassphraseFileFetcher_WithStdinAndMultipleFetches(t *testing.T) {
for range 4 {
fetcher, err := testPkg.passphraseFileFetcher("-", stdin)
if err != nil {
t.Errorf("Expected passphraseFileFetcher to not return an error, but got %v", err)
}
require.NoError(t, err, "Expected passphraseFileFetcher to not return an error")
pass, err := fetcher("key")
if err != nil {
t.Errorf("Expected passphraseFileFetcher invocation to succeed, failed with %v", err)
}
require.NoError(t, err, "Expected passphraseFileFetcher invocation to succeed")
if string(pass) != string(passphrase) {
t.Errorf("Expected multiple passphrase fetch to return %q, got %q", passphrase, pass)
}
assert.Equal(t, string(passphrase), string(pass), "Expected multiple passphrase fetch to return %q, got %q", passphrase, pass)
}
}
@ -148,9 +124,7 @@ func TestValidateVersion(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := validateVersion(tt.args.ver); err != nil {
if !errors.Is(err, tt.wantErr) {
t.Errorf("Expected {%v}, got {%v}", tt.wantErr, err)
}
assert.ErrorIs(t, err, tt.wantErr)
}
})
}

@ -48,9 +48,7 @@ func TestPullSetRegistryClient(t *testing.T) {
func TestPullRun_ChartNotFound(t *testing.T) {
srv, err := startLocalServerForTests(t, nil)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
defer srv.Close()
config := actionConfigFixture(t)

@ -190,8 +190,7 @@ func TestGetContainerLogs_MultipleContainers(t *testing.T) {
rt := &ReleaseTesting{Namespace: "default"}
var buf bytes.Buffer
err := rt.getContainerLogs(&buf, client, "test-pod")
require.NoError(t, err)
require.NoError(t, rt.getContainerLogs(&buf, client, "test-pod"))
output := buf.String()
assert.Contains(t, output, "POD LOGS: test-pod (main)")
assert.Contains(t, output, "POD LOGS: test-pod (sidecar)")
@ -217,8 +216,7 @@ func TestGetContainerLogs_WithInitContainers(t *testing.T) {
rt := &ReleaseTesting{Namespace: "default"}
var buf bytes.Buffer
err := rt.getContainerLogs(&buf, client, "test-pod")
require.NoError(t, err)
require.NoError(t, rt.getContainerLogs(&buf, client, "test-pod"))
output := buf.String()
// Init containers should appear before regular containers
assert.Contains(t, output, "POD LOGS: test-pod (init-setup)")
@ -230,8 +228,7 @@ func TestGetContainerLogs_PodNotFound(t *testing.T) {
rt := &ReleaseTesting{Namespace: "default"}
var buf bytes.Buffer
err := rt.getContainerLogs(&buf, client, "nonexistent-pod")
assert.ErrorContains(t, err, "unable to get pod nonexistent-pod")
assert.ErrorContains(t, rt.getContainerLogs(&buf, client, "nonexistent-pod"), "unable to get pod nonexistent-pod")
}
func TestGetContainerLogs_OutputHeaderFormat(t *testing.T) {
@ -252,8 +249,7 @@ func TestGetContainerLogs_OutputHeaderFormat(t *testing.T) {
rt := &ReleaseTesting{Namespace: "default"}
var buf bytes.Buffer
err := rt.getContainerLogs(&buf, client, "multi-test")
require.NoError(t, err)
require.NoError(t, rt.getContainerLogs(&buf, client, "multi-test"))
output := buf.String()
assert.Contains(t, output, "POD LOGS: multi-test (container-a)")
assert.Contains(t, output, "POD LOGS: multi-test (container-b)")

@ -78,8 +78,7 @@ func TestRollback_WaitOptionsPassedDownstream(t *testing.T) {
// Access the underlying FailingKubeClient to check recorded options
failer := config.KubeClient.(*kubefake.FailingKubeClient)
err := client.Run(rel.Name)
req.NoError(err)
req.NoError(client.Run(rel.Name))
// Verify that WaitOptions were passed to GetWaiter
is.NotEmpty(failer.RecordedWaitOptions, "WaitOptions should be passed to GetWaiter")

@ -21,6 +21,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/chart/common"
chart "helm.sh/helm/v4/pkg/chart/v2"
@ -47,9 +48,7 @@ func TestShow(t *testing.T) {
}
output, err := client.Run("")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
expect := `name: alpine
@ -69,9 +68,7 @@ bar
baz
`
if output != expect {
t.Errorf("Expected\n%q\nGot\n%q\n", expect, output)
}
assert.Equal(t, expect, output, "Expected\n%q\nGot\n%q\n", expect, output)
}
func TestShowNoValues(t *testing.T) {
@ -82,13 +79,9 @@ func TestShowNoValues(t *testing.T) {
// Regression tests for missing values. See issue #1024.
client.OutputFormat = ShowValues
output, err := client.Run("")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
if output != "" {
t.Errorf("expected empty values buffer, got %s", output)
}
assert.Empty(t, output, "expected empty values buffer, got %s", output)
}
func TestShowValuesByJsonPathFormat(t *testing.T) {
@ -97,13 +90,9 @@ func TestShowValuesByJsonPathFormat(t *testing.T) {
client.JSONPathTemplate = "{$.nestedKey.simpleKey}"
client.chart = buildChart(withSampleValues())
output, err := client.Run("")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
expect := "simpleValue"
if output != expect {
t.Errorf("Expected\n%q\nGot\n%q\n", expect, output)
}
assert.Equal(t, expect, output, "Expected\n%q\nGot\n%q\n", expect, output)
}
func TestShowCRDs(t *testing.T) {
@ -121,9 +110,7 @@ func TestShowCRDs(t *testing.T) {
}
output, err := client.Run("")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
expect := `---
foo
@ -135,9 +122,7 @@ bar
baz
`
if output != expect {
t.Errorf("Expected\n%q\nGot\n%q\n", expect, output)
}
assert.Equal(t, expect, output, "Expected\n%q\nGot\n%q\n", expect, output)
}
func TestShowNoReadme(t *testing.T) {
@ -154,9 +139,7 @@ func TestShowNoReadme(t *testing.T) {
}
output, err := client.Run("")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
expect := `name: alpine
@ -167,9 +150,7 @@ foo
bar
`
if output != expect {
t.Errorf("Expected\n%q\nGot\n%q\n", expect, output)
}
assert.Equal(t, expect, output, "Expected\n%q\nGot\n%q\n", expect, output)
}
func TestShowSetRegistryClient(t *testing.T) {

@ -121,7 +121,7 @@ func TestUninstallRelease_Wait(t *testing.T) {
unAction.cfg.KubeClient = failer
resi, err := unAction.Run(rel.Name)
req.Error(err)
is.Contains(err.Error(), "U timed out")
req.ErrorContains(err, "U timed out")
res, err := releaserToV1Release(resi.Release)
req.NoError(err)
is.Equal(common.StatusUninstalled, res.Info.Status)
@ -163,7 +163,7 @@ func TestUninstallRelease_Cascade(t *testing.T) {
unAction.cfg.KubeClient = failer
_, err := unAction.Run(rel.Name)
require.Error(t, err)
is.Contains(err.Error(), "failed to delete release: come-fail-away")
is.ErrorContains(err, "failed to delete release: come-fail-away")
}
func TestUninstallRun_UnreachableKubeClient(t *testing.T) {

@ -176,8 +176,8 @@ func TestUpgradeRelease_RollbackOnFailure(t *testing.T) {
resi, err := upAction.Run(rel.Name, buildChart(), vals)
req.Error(err)
is.Contains(err.Error(), "arming key removed")
is.Contains(err.Error(), "rollback-on-failure")
req.ErrorContains(err, "arming key removed")
req.ErrorContains(err, "rollback-on-failure")
res, err := releaserToV1Release(resi)
req.NoError(err)
@ -207,8 +207,8 @@ func TestUpgradeRelease_RollbackOnFailure(t *testing.T) {
_, err := upAction.Run(rel.Name, buildChart(), vals)
req.Error(err)
is.Contains(err.Error(), "update fail")
is.Contains(err.Error(), "an error occurred while rolling back the release")
req.ErrorContains(err, "update fail")
is.ErrorContains(err, "an error occurred while rolling back the release")
})
}
@ -240,8 +240,7 @@ func TestUpgradeRelease_ReuseValues(t *testing.T) {
rel.Info.Status = common.StatusDeployed
rel.Config = existingValues
err := upAction.cfg.Releases.Create(rel)
req.NoError(err)
req.NoError(upAction.cfg.Releases.Create(rel))
upAction.ReuseValues = true
// setting newValues and upgrading
@ -253,11 +252,8 @@ func TestUpgradeRelease_ReuseValues(t *testing.T) {
// Now make sure it is actually upgraded
updatedResi, err := upAction.cfg.Releases.Get(res.Name, 2)
req.NoError(err)
req.NotNil(updatedResi, "Updated Release is nil")
if updatedResi == nil {
is.Fail("Updated Release is nil")
return
}
updatedRes, err := releaserToV1Release(updatedResi)
req.NoError(err)
@ -303,8 +299,7 @@ func TestUpgradeRelease_ReuseValues(t *testing.T) {
Config: existingValues,
Version: 1,
}
err := upAction.cfg.Releases.Create(rel)
req.NoError(err)
req.NoError(upAction.cfg.Releases.Create(rel))
upAction.ReuseValues = true
sampleChartWithSubChart := buildChart(
@ -322,11 +317,8 @@ func TestUpgradeRelease_ReuseValues(t *testing.T) {
// Now get the upgraded release
updatedResi, err := upAction.cfg.Releases.Get(res.Name, 2)
req.NoError(err)
req.NotNil(updatedResi, "Updated Release is nil")
if updatedResi == nil {
is.Fail("Updated Release is nil")
return
}
updatedRes, err := releaserToV1Release(updatedResi)
req.NoError(err)
@ -373,8 +365,7 @@ func TestUpgradeRelease_ResetThenReuseValues(t *testing.T) {
rel.Info.Status = common.StatusDeployed
rel.Config = existingValues
err := upAction.cfg.Releases.Create(rel)
req.NoError(err)
req.NoError(upAction.cfg.Releases.Create(rel))
upAction.ResetThenReuseValues = true
// setting newValues and upgrading
@ -386,11 +377,8 @@ func TestUpgradeRelease_ResetThenReuseValues(t *testing.T) {
// Now make sure it is actually upgraded
updatedResi, err := upAction.cfg.Releases.Get(res.Name, 2)
req.NoError(err)
req.NotNil(updatedResi, "Updated Release is nil")
if updatedResi == nil {
is.Fail("Updated Release is nil")
return
}
updatedRes, err := releaserToV1Release(updatedResi)
req.NoError(err)
@ -417,7 +405,7 @@ func TestUpgradeRelease_Pending(t *testing.T) {
vals := map[string]any{}
_, err := upAction.Run(rel.Name, buildChart(), vals)
req.Contains(err.Error(), "progress", err)
req.ErrorContains(err, "progress")
}
func TestUpgradeRelease_Interrupted_Wait(t *testing.T) {
@ -470,7 +458,7 @@ func TestUpgradeRelease_Interrupted_RollbackOnFailure(t *testing.T) {
resi, err := upAction.RunWithContext(ctx, rel.Name, buildChart(), vals)
req.Error(err)
is.Contains(err.Error(), "release interrupted-release failed, and has been rolled back due to rollback-on-failure being set: context canceled")
req.ErrorContains(err, "release interrupted-release failed, and has been rolled back due to rollback-on-failure being set: context canceled")
res, err := releaserToV1Release(resi)
req.NoError(err)
// Now make sure it is actually upgraded
@ -491,9 +479,8 @@ func TestMergeCustomLabels(t *testing.T) {
{map[string]string{"k1": "v1", "k2": "v2"}, map[string]string{"k1": "null", "k2": "v3"}, map[string]string{"k2": "v3"}},
}
for _, test := range tests {
if output := mergeCustomLabels(test[0], test[1]); !reflect.DeepEqual(test[2], output) {
t.Errorf("Expected {%v}, got {%v}", test[2], output)
}
output := mergeCustomLabels(test[0], test[1])
assert.Truef(t, reflect.DeepEqual(test[2], output), "Expected {%v}, got {%v}", test[2], output)
}
}
@ -511,8 +498,7 @@ func TestUpgradeRelease_Labels(t *testing.T) {
}
rel.Info.Status = common.StatusDeployed
err := upAction.cfg.Releases.Create(rel)
req.NoError(err)
req.NoError(upAction.cfg.Releases.Create(rel))
upAction.Labels = map[string]string{
"key1": "null",
@ -528,11 +514,8 @@ func TestUpgradeRelease_Labels(t *testing.T) {
// Now make sure it is actually upgraded and labels were merged
updatedResi, err := upAction.cfg.Releases.Get(res.Name, 2)
req.NoError(err)
req.NotNil(updatedResi, "Updated Release is nil")
if updatedResi == nil {
is.Fail("Updated Release is nil")
return
}
updatedRes, err := releaserToV1Release(updatedResi)
req.NoError(err)
is.Equal(common.StatusDeployed, updatedRes.Info.Status)
@ -541,11 +524,8 @@ func TestUpgradeRelease_Labels(t *testing.T) {
// Now make sure it is suppressed release still contains original labels
initialResi, err := upAction.cfg.Releases.Get(res.Name, 1)
req.NoError(err)
req.NotNil(initialResi, "Initial Release is nil")
if initialResi == nil {
is.Fail("Updated Release is nil")
return
}
initialRes, err := releaserToV1Release(initialResi)
req.NoError(err)
is.Equal(common.StatusSuperseded, initialRes.Info.Status)
@ -566,8 +546,7 @@ func TestUpgradeRelease_SystemLabels(t *testing.T) {
}
rel.Info.Status = common.StatusDeployed
err := upAction.cfg.Releases.Create(rel)
req.NoError(err)
req.NoError(upAction.cfg.Releases.Create(rel))
upAction.Labels = map[string]string{
"key1": "null",
@ -575,12 +554,10 @@ func TestUpgradeRelease_SystemLabels(t *testing.T) {
"owner": "val3",
}
// setting newValues and upgrading
_, err = upAction.Run(rel.Name, buildChart(), nil)
if err == nil {
t.Fatal("expected an error")
}
_, err := upAction.Run(rel.Name, buildChart(), nil)
require.Error(t, err, "expected an error")
is.Equal(fmt.Errorf("user supplied labels contains system reserved label name. System labels: %+v", driver.GetSystemLabels()), err)
is.EqualError(err, fmt.Sprintf("user supplied labels contains system reserved label name. System labels: %+v", driver.GetSystemLabels()))
}
func TestUpgradeRelease_DryRun(t *testing.T) {

@ -132,7 +132,7 @@ func TestRequireAdoption(t *testing.T) {
// Verify that a resource that lacks labels/annotations can be adopted
found, err := requireAdoption(resources)
require.NoError(t, err)
assert.Len(t, found, 1)
require.Len(t, found, 1)
assert.Equal(t, found[0], existing)
assert.NotSame(t, found[0], existing)
}
@ -157,7 +157,7 @@ func TestExistingResourceConflict(t *testing.T) {
// Verify only existing resources are returned
found, err := existingResourceConflict(resources, releaseName, releaseNamespace)
require.NoError(t, err)
assert.Len(t, found, 1)
require.Len(t, found, 1)
assert.Equal(t, found[0], existing)
assert.NotSame(t, found[0], existing)
@ -171,45 +171,38 @@ func TestCheckOwnership(t *testing.T) {
deployFoo := newDeploymentResource("foo", "ns-a", "")
// Verify that a resource that lacks labels/annotations is not owned
err := checkOwnership(deployFoo.Object, "rel-a", "ns-a")
require.EqualError(t, err, `invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by": must be set to "Helm"; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "rel-a"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "ns-a"`)
require.EqualError(t, checkOwnership(deployFoo.Object, "rel-a", "ns-a"), `invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by": must be set to "Helm"; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "rel-a"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "ns-a"`)
// Set managed by label and verify annotation error message
_ = accessor.SetLabels(deployFoo.Object, map[string]string{
appManagedByLabel: appManagedByHelm,
})
err = checkOwnership(deployFoo.Object, "rel-a", "ns-a")
require.EqualError(t, err, `invalid ownership metadata; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "rel-a"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "ns-a"`)
require.EqualError(t, checkOwnership(deployFoo.Object, "rel-a", "ns-a"), `invalid ownership metadata; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "rel-a"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "ns-a"`)
// Set only the release name annotation and verify missing release namespace error message
_ = accessor.SetAnnotations(deployFoo.Object, map[string]string{
helmReleaseNameAnnotation: "rel-a",
})
err = checkOwnership(deployFoo.Object, "rel-a", "ns-a")
require.EqualError(t, err, `invalid ownership metadata; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "ns-a"`)
require.EqualError(t, checkOwnership(deployFoo.Object, "rel-a", "ns-a"), `invalid ownership metadata; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "ns-a"`)
// Set both release name and namespace annotations and verify no ownership errors
_ = accessor.SetAnnotations(deployFoo.Object, map[string]string{
helmReleaseNameAnnotation: "rel-a",
helmReleaseNamespaceAnnotation: "ns-a",
})
err = checkOwnership(deployFoo.Object, "rel-a", "ns-a")
require.NoError(t, err)
require.NoError(t, checkOwnership(deployFoo.Object, "rel-a", "ns-a"))
// Verify ownership error for wrong release name
err = checkOwnership(deployFoo.Object, "rel-b", "ns-a")
require.EqualError(t, err, `invalid ownership metadata; annotation validation error: key "meta.helm.sh/release-name" must equal "rel-b": current value is "rel-a"`)
require.EqualError(t, checkOwnership(deployFoo.Object, "rel-b", "ns-a"), `invalid ownership metadata; annotation validation error: key "meta.helm.sh/release-name" must equal "rel-b": current value is "rel-a"`)
// Verify ownership error for wrong release namespace
err = checkOwnership(deployFoo.Object, "rel-a", "ns-b")
require.EqualError(t, err, `invalid ownership metadata; annotation validation error: key "meta.helm.sh/release-namespace" must equal "ns-b": current value is "ns-a"`)
require.EqualError(t, checkOwnership(deployFoo.Object, "rel-a", "ns-b"), `invalid ownership metadata; annotation validation error: key "meta.helm.sh/release-namespace" must equal "ns-b": current value is "ns-a"`)
// Verify ownership error for wrong manager label
_ = accessor.SetLabels(deployFoo.Object, map[string]string{
appManagedByLabel: "helm",
})
err = checkOwnership(deployFoo.Object, "rel-a", "ns-a")
assert.EqualError(t, err, `invalid ownership metadata; label validation error: key "app.kubernetes.io/managed-by" must equal "Helm": current value is "helm"`)
assert.EqualError(t, checkOwnership(deployFoo.Object, "rel-a", "ns-a"), `invalid ownership metadata; label validation error: key "app.kubernetes.io/managed-by" must equal "Helm": current value is "helm"`)
}
func TestVerifyOwnershipBeforeDelete(t *testing.T) {
@ -249,8 +242,8 @@ func TestVerifyOwnershipBeforeDelete(t *testing.T) {
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
require.NoError(t, err)
assert.Len(t, ownedList, 1)
assert.Len(t, unownedList, 1)
require.Len(t, ownedList, 1)
require.Len(t, unownedList, 1)
assert.Equal(t, "owned", ownedList[0].Name)
assert.Equal(t, "unowned", unownedList[0].Name)
})
@ -297,8 +290,8 @@ func TestVerifyOwnershipBeforeDelete(t *testing.T) {
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
require.NoError(t, err)
assert.Len(t, ownedList, 1)
assert.Len(t, unownedList, 1)
require.Len(t, ownedList, 1)
require.Len(t, unownedList, 1)
assert.Equal(t, "owned", ownedList[0].Name)
assert.Equal(t, "unowned", unownedList[0].Name)
})
@ -306,32 +299,26 @@ func TestVerifyOwnershipBeforeDelete(t *testing.T) {
func TestSetMetadataVisitor(t *testing.T) {
var (
err error
deployFoo = newDeploymentResource("foo", "ns-a", "")
deployBar = newDeploymentResource("bar", "ns-a-system", "")
resources = kube.ResourceList{deployFoo, deployBar}
)
// Set release tracking metadata and verify no error
err = resources.Visit(setMetadataVisitor("rel-a", "ns-a", true))
require.NoError(t, err)
require.NoError(t, resources.Visit(setMetadataVisitor("rel-a", "ns-a", true)))
// Verify that release "b" cannot take ownership of "a"
err = resources.Visit(setMetadataVisitor("rel-b", "ns-a", false))
require.Error(t, err)
require.Error(t, resources.Visit(setMetadataVisitor("rel-b", "ns-a", false)))
// Force release "b" to take ownership
err = resources.Visit(setMetadataVisitor("rel-b", "ns-a", true))
require.NoError(t, err)
require.NoError(t, resources.Visit(setMetadataVisitor("rel-b", "ns-a", true)))
// Check that there is now no ownership error when setting metadata without force
err = resources.Visit(setMetadataVisitor("rel-b", "ns-a", false))
require.NoError(t, err)
require.NoError(t, resources.Visit(setMetadataVisitor("rel-b", "ns-a", false)))
// Add a new resource that is missing ownership metadata and verify error
resources.Append(newDeploymentResource("baz", "default", ""))
err = resources.Visit(setMetadataVisitor("rel-b", "ns-a", false))
assert.ErrorContains(t, err, `Deployment "baz" in namespace "" cannot be owned`)
assert.ErrorContains(t, resources.Visit(setMetadataVisitor("rel-b", "ns-a", false)), `Deployment "baz" in namespace "" cannot be owned`)
}
func TestValidateNameAndGenerateName(t *testing.T) {

@ -17,68 +17,44 @@ package common
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestVersionSet(t *testing.T) {
vs := VersionSet{"v1", "apps/v1"}
if d := len(vs); d != 2 {
t.Errorf("Expected 2 versions, got %d", d)
}
d := len(vs)
assert.Equalf(t, 2, d, "Expected 2 versions, got %d", d)
if !vs.Has("apps/v1") {
t.Error("Expected to find apps/v1")
}
assert.True(t, vs.Has("apps/v1"), "Expected to find apps/v1")
if vs.Has("Spanish/inquisition") {
t.Error("No one expects the Spanish/inquisition")
}
assert.False(t, vs.Has("Spanish/inquisition"), "No one expects the Spanish/inquisition")
}
func TestDefaultVersionSet(t *testing.T) {
if !DefaultVersionSet.Has("v1") {
t.Error("Expected core v1 version set")
}
assert.True(t, DefaultVersionSet.Has("v1"), "Expected core v1 version set")
}
func TestDefaultCapabilities(t *testing.T) {
caps := DefaultCapabilities
kv := caps.KubeVersion
if kv.String() != "v1.20.0" {
t.Errorf("Expected default KubeVersion.String() to be v1.20.0, got %q", kv.String())
}
if kv.Version != "v1.20.0" {
t.Errorf("Expected default KubeVersion.Version to be v1.20.0, got %q", kv.Version)
}
if kv.GitVersion() != "v1.20.0" {
t.Errorf("Expected default KubeVersion.GitVersion() to be v1.20.0, got %q", kv.Version)
}
if kv.Major != "1" {
t.Errorf("Expected default KubeVersion.Major to be 1, got %q", kv.Major)
}
if kv.Minor != "20" {
t.Errorf("Expected default KubeVersion.Minor to be 20, got %q", kv.Minor)
}
assert.Equalf(t, "v1.20.0", kv.String(), "Expected default KubeVersion.String() to be v1.20.0, got %q", kv.String())
assert.Equalf(t, "v1.20.0", kv.Version, "Expected default KubeVersion.Version to be v1.20.0, got %q", kv.Version)
assert.Equalf(t, "v1.20.0", kv.GitVersion(), "Expected default KubeVersion.GitVersion() to be v1.20.0, got %q", kv.Version)
assert.Equalf(t, "1", kv.Major, "Expected default KubeVersion.Major to be 1, got %q", kv.Major)
assert.Equalf(t, "20", kv.Minor, "Expected default KubeVersion.Minor to be 20, got %q", kv.Minor)
hv := caps.HelmVersion
if hv.Version != "v4.2" {
t.Errorf("Expected default HelmVersion to be v4.2, got %q", hv.Version)
}
assert.Equalf(t, "v4.2", hv.Version, "Expected default HelmVersion to be v4.2, got %q", hv.Version)
}
func TestParseKubeVersion(t *testing.T) {
kv, err := ParseKubeVersion("v1.16.0")
if err != nil {
t.Error("Expected v1.16.0 to parse successfully")
}
if kv.Version != "v1.16.0" {
t.Errorf("Expected parsed KubeVersion.Version to be v1.16.0, got %q", kv.String())
}
if kv.Major != "1" {
t.Errorf("Expected parsed KubeVersion.Major to be 1, got %q", kv.Major)
}
if kv.Minor != "16" {
t.Errorf("Expected parsed KubeVersion.Minor to be 16, got %q", kv.Minor)
}
require.NoError(t, err, "Expected v1.16.0 to parse successfully")
assert.Equalf(t, "v1.16.0", kv.Version, "Expected parsed KubeVersion.Version to be v1.16.0, got %q", kv.String())
assert.Equalf(t, "1", kv.Major, "Expected parsed KubeVersion.Major to be 1, got %q", kv.Major)
assert.Equalf(t, "16", kv.Minor, "Expected parsed KubeVersion.Minor to be 16, got %q", kv.Minor)
}
func TestParseKubeVersionWithVendorSuffixes(t *testing.T) {
@ -101,21 +77,11 @@ func TestParseKubeVersionWithVendorSuffixes(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kv, err := ParseKubeVersion(tt.input)
if err != nil {
t.Fatalf("ParseKubeVersion() error = %v", err)
}
if kv.Version != tt.wantVer {
t.Errorf("Version = %q, want %q", kv.Version, tt.wantVer)
}
if kv.String() != tt.wantString {
t.Errorf("String() = %q, want %q", kv.String(), tt.wantString)
}
if kv.Major != tt.wantMajor {
t.Errorf("Major = %q, want %q", kv.Major, tt.wantMajor)
}
if kv.Minor != tt.wantMinor {
t.Errorf("Minor = %q, want %q", kv.Minor, tt.wantMinor)
}
require.NoErrorf(t, err, "ParseKubeVersion()")
assert.Equalf(t, tt.wantVer, kv.Version, "Version = %q, want %q", kv.Version, tt.wantVer)
assert.Equalf(t, tt.wantString, kv.String(), "String() = %q, want %q", kv.String(), tt.wantString)
assert.Equalf(t, tt.wantMajor, kv.Major, "Major = %q, want %q", kv.Major, tt.wantMajor)
assert.Equalf(t, tt.wantMinor, kv.Minor, "Minor = %q, want %q", kv.Minor, tt.wantMinor)
})
}
}

@ -141,9 +141,7 @@ func TestCoalesceValues(t *testing.T) {
)
vals, err := common.ReadValues(testCoalesceValuesYaml)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
// taking a copy of the values before passing it
// to CoalesceValues as argument, so that we can
@ -152,9 +150,7 @@ func TestCoalesceValues(t *testing.T) {
maps.Copy(valsCopy, vals)
v, err := CoalesceValues(c, vals)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
j, _ := json.MarshalIndent(v, "", " ")
t.Logf("Coalesced Values: %s", string(j))
@ -211,32 +207,26 @@ func TestCoalesceValues(t *testing.T) {
nullKeys := []string{"bottom", "right", "left", "front"}
for _, nullKey := range nullKeys {
if _, ok := v[nullKey]; ok {
t.Errorf("Expected key %q to be removed, still present", nullKey)
}
_, ok := v[nullKey]
assert.Falsef(t, ok, "Expected key %q to be removed, still present", nullKey)
}
if _, ok := v["nested"].(map[string]any)["boat"]; ok {
t.Error("Expected nested boat key to be removed, still present")
}
_, ok := v["nested"].(map[string]any)["boat"]
assert.False(t, ok, "Expected nested boat key to be removed, still present")
subchart := v["pequod"].(map[string]any)
if _, ok := subchart["boat"]; ok {
t.Error("Expected subchart boat key to be removed, still present")
}
_, ok = subchart["boat"]
assert.False(t, ok, "Expected subchart boat key to be removed, still present")
subsubchart := subchart["ahab"].(map[string]any)
if _, ok := subsubchart["boat"]; ok {
t.Error("Expected sub-subchart ahab boat key to be removed, still present")
}
_, ok = subsubchart["boat"]
assert.False(t, ok, "Expected sub-subchart ahab boat key to be removed, still present")
if _, ok := subsubchart["nested"].(map[string]any)["boat"]; ok {
t.Error("Expected sub-subchart nested boat key to be removed, still present")
}
_, ok = subsubchart["nested"].(map[string]any)["boat"]
assert.False(t, ok, "Expected sub-subchart nested boat key to be removed, still present")
if _, ok := subsubchart["object"]; ok {
t.Error("Expected sub-subchart object map to be removed, still present")
}
_, ok = subsubchart["object"]
assert.False(t, ok, "Expected sub-subchart object map to be removed, still present")
// CoalesceValues should not mutate the passed arguments
is.Equal(valsCopy, vals)
@ -306,9 +296,7 @@ func TestMergeValues(t *testing.T) {
)
vals, err := common.ReadValues(testCoalesceValuesYaml)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
// taking a copy of the values before passing it
// to MergeValues as argument, so that we can
@ -317,9 +305,7 @@ func TestMergeValues(t *testing.T) {
maps.Copy(valsCopy, vals)
v, err := MergeValues(c, vals)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
j, _ := json.MarshalIndent(v, "", " ")
t.Logf("Coalesced Values: %s", string(j))
@ -377,25 +363,19 @@ func TestMergeValues(t *testing.T) {
// removed.
nullKeys := []string{"bottom", "right", "left", "front"}
for _, nullKey := range nullKeys {
if vv, ok := v[nullKey]; !ok {
t.Errorf("Expected key %q to be present but it was removed", nullKey)
} else if vv != nil {
t.Errorf("Expected key %q to be null but it has a value of %v", nullKey, vv)
}
vv, ok := v[nullKey]
assert.Truef(t, ok, "Expected key %q to be present but it was removed", nullKey)
assert.Nilf(t, vv, "Expected key %q to be null but it has a value of %v", nullKey, vv)
}
if _, ok := v["nested"].(map[string]any)["boat"]; !ok {
t.Error("Expected nested boat key to be present but it was removed")
}
_, ok := v["nested"].(map[string]any)["boat"]
assert.True(t, ok, "Expected nested boat key to be present but it was removed")
subchart := v["pequod"].(map[string]any)["ahab"].(map[string]any)
if _, ok := subchart["boat"]; !ok {
t.Error("Expected subchart boat key to be present but it was removed")
}
assert.Contains(t, subchart, "boat", "Expected subchart boat key to be present but it was removed")
if _, ok := subchart["nested"].(map[string]any)["bar"]; !ok {
t.Error("Expected subchart nested bar key to be present but it was removed")
}
_, ok = subchart["nested"].(map[string]any)["bar"]
assert.True(t, ok, "Expected subchart nested bar key to be present but it was removed")
// CoalesceValues should not mutate the passed arguments
is.Equal(valsCopy, vals)
@ -433,47 +413,27 @@ func TestCoalesceTables(t *testing.T) {
// otherwise the values are coalesced.
CoalesceTables(dst, src)
if dst["name"] != "Ishmael" {
t.Errorf("Unexpected name: %s", dst["name"])
}
if dst["occupation"] != "whaler" {
t.Errorf("Unexpected occupation: %s", dst["occupation"])
}
assert.Equal(t, "Ishmael", dst["name"], "Unexpected name: %s", dst["name"])
assert.Equal(t, "whaler", dst["occupation"], "Unexpected occupation: %s", dst["occupation"])
addr, ok := dst["address"].(map[string]any)
if !ok {
t.Fatal("Address went away.")
}
if addr["street"].(string) != "123 Spouter Inn Ct." {
t.Errorf("Unexpected address: %v", addr["street"])
}
require.True(t, ok, "Address went away.")
assert.Equal(t, "123 Spouter Inn Ct.", addr["street"].(string), "Unexpected address: %v", addr["street"])
assert.Equal(t, "Nantucket", addr["city"].(string), "Unexpected city: %v", addr["city"])
assert.Equal(t, "MA", addr["state"].(string), "Unexpected state: %v", addr["state"])
if addr["city"].(string) != "Nantucket" {
t.Errorf("Unexpected city: %v", addr["city"])
}
_, ok = addr["country"]
assert.False(t, ok, "The country is not left out.")
if addr["state"].(string) != "MA" {
t.Errorf("Unexpected state: %v", addr["state"])
}
det, ok := dst["details"].(map[string]any)
require.Truef(t, ok, "Details is the wrong type: %v", dst["details"])
if _, ok = addr["country"]; ok {
t.Error("The country is not left out.")
}
_, ok = det["friends"]
assert.True(t, ok, "Could not find your friends. Maybe you don't have any. :-(")
assert.Equal(t, "pequod", dst["boat"].(string), "Expected boat string, got %v", dst["boat"])
if det, ok := dst["details"].(map[string]any); !ok {
t.Fatalf("Details is the wrong type: %v", dst["details"])
} else if _, ok := det["friends"]; !ok {
t.Error("Could not find your friends. Maybe you don't have any. :-(")
}
if dst["boat"].(string) != "pequod" {
t.Errorf("Expected boat string, got %v", dst["boat"])
}
if _, ok = dst["hole"]; ok {
t.Error("The hole still exists.")
}
_, ok = dst["hole"]
assert.False(t, ok, "The hole still exists.")
dst2 := map[string]any{
"name": "Ishmael",
@ -493,40 +453,21 @@ func TestCoalesceTables(t *testing.T) {
// this happens when the --reuse-values flag is set but the chart has no modifications yet
CoalesceTables(dst2, nil)
if dst2["name"] != "Ishmael" {
t.Errorf("Unexpected name: %s", dst2["name"])
}
assert.Equal(t, "Ishmael", dst2["name"], "Unexpected name: %s", dst2["name"])
addr2, ok := dst2["address"].(map[string]any)
if !ok {
t.Fatal("Address went away.")
}
if addr2["street"].(string) != "123 Spouter Inn Ct." {
t.Errorf("Unexpected address: %v", addr2["street"])
}
if addr2["city"].(string) != "Nantucket" {
t.Errorf("Unexpected city: %v", addr2["city"])
}
if addr2["country"].(string) != "US" {
t.Errorf("Unexpected Country: %v", addr2["country"])
}
if det2, ok := dst2["details"].(map[string]any); !ok {
t.Fatalf("Details is the wrong type: %v", dst2["details"])
} else if _, ok := det2["friends"]; !ok {
t.Error("Could not find your friends. Maybe you don't have any. :-(")
}
if dst2["boat"].(string) != "pequod" {
t.Errorf("Expected boat string, got %v", dst2["boat"])
}
if dst2["hole"].(string) != "black" {
t.Errorf("Expected hole string, got %v", dst2["boat"])
}
require.True(t, ok, "Address went away.")
assert.Equal(t, "123 Spouter Inn Ct.", addr2["street"].(string), "Unexpected address: %v", addr2["street"])
assert.Equal(t, "Nantucket", addr2["city"].(string), "Unexpected city: %v", addr2["city"])
assert.Equal(t, "US", addr2["country"].(string), "Unexpected Country: %v", addr2["country"])
det2, ok := dst2["details"].(map[string]any)
require.Truef(t, ok, "Details is the wrong type: %v", dst2["details"])
_, ok = det2["friends"]
assert.True(t, ok, "Could not find your friends. Maybe you don't have any. :-(")
assert.Equal(t, "pequod", dst2["boat"].(string), "Expected boat string, got %v", dst2["boat"])
assert.Equal(t, "black", dst2["hole"].(string), "Expected hole string, got %v", dst2["boat"])
}
func TestMergeTables(t *testing.T) {
@ -561,51 +502,30 @@ func TestMergeTables(t *testing.T) {
// otherwise the values are coalesced.
MergeTables(dst, src)
if dst["name"] != "Ishmael" {
t.Errorf("Unexpected name: %s", dst["name"])
}
if dst["occupation"] != "whaler" {
t.Errorf("Unexpected occupation: %s", dst["occupation"])
}
assert.Equal(t, "Ishmael", dst["name"], "Unexpected name: %s", dst["name"])
assert.Equal(t, "whaler", dst["occupation"], "Unexpected occupation: %s", dst["occupation"])
addr, ok := dst["address"].(map[string]any)
if !ok {
t.Fatal("Address went away.")
}
if addr["street"].(string) != "123 Spouter Inn Ct." {
t.Errorf("Unexpected address: %v", addr["street"])
}
if addr["city"].(string) != "Nantucket" {
t.Errorf("Unexpected city: %v", addr["city"])
}
if addr["state"].(string) != "MA" {
t.Errorf("Unexpected state: %v", addr["state"])
}
require.True(t, ok, "Address went away.")
assert.Equal(t, "123 Spouter Inn Ct.", addr["street"].(string), "Unexpected address: %v", addr["street"])
assert.Equal(t, "Nantucket", addr["city"].(string), "Unexpected city: %v", addr["city"])
assert.Equal(t, "MA", addr["state"].(string), "Unexpected state: %v", addr["state"])
// This is one test that is different from CoalesceTables. Because country
// is a nil value and it's not removed it's still present.
if _, ok = addr["country"]; !ok {
t.Error("The country is left out.")
}
_, ok = addr["country"]
assert.True(t, ok, "The country is left out.")
if det, ok := dst["details"].(map[string]any); !ok {
t.Fatalf("Details is the wrong type: %v", dst["details"])
} else if _, ok := det["friends"]; !ok {
t.Error("Could not find your friends. Maybe you don't have any. :-(")
}
det, ok := dst["details"].(map[string]any)
require.Truef(t, ok, "Details is the wrong type: %v", dst["details"])
if dst["boat"].(string) != "pequod" {
t.Errorf("Expected boat string, got %v", dst["boat"])
}
_, ok = det["friends"]
assert.True(t, ok, "Could not find your friends. Maybe you don't have any. :-(")
assert.Equal(t, "pequod", dst["boat"].(string), "Expected boat string, got %v", dst["boat"])
// This is one test that is different from CoalesceTables. Because hole
// is a nil value and it's not removed it's still present.
if _, ok = dst["hole"]; !ok {
t.Error("The hole no longer exists.")
}
assert.Contains(t, dst, "hole", "The hole no longer exists.")
dst2 := map[string]any{
"name": "Ishmael",
@ -626,44 +546,21 @@ func TestMergeTables(t *testing.T) {
// this happens when the --reuse-values flag is set but the chart has no modifications yet
MergeTables(dst2, nil)
if dst2["name"] != "Ishmael" {
t.Errorf("Unexpected name: %s", dst2["name"])
}
assert.Equal(t, "Ishmael", dst2["name"], "Unexpected name: %s", dst2["name"])
addr2, ok := dst2["address"].(map[string]any)
if !ok {
t.Fatal("Address went away.")
}
if addr2["street"].(string) != "123 Spouter Inn Ct." {
t.Errorf("Unexpected address: %v", addr2["street"])
}
if addr2["city"].(string) != "Nantucket" {
t.Errorf("Unexpected city: %v", addr2["city"])
}
if addr2["country"].(string) != "US" {
t.Errorf("Unexpected Country: %v", addr2["country"])
}
if det2, ok := dst2["details"].(map[string]any); !ok {
t.Fatalf("Details is the wrong type: %v", dst2["details"])
} else if _, ok := det2["friends"]; !ok {
t.Error("Could not find your friends. Maybe you don't have any. :-(")
}
if dst2["boat"].(string) != "pequod" {
t.Errorf("Expected boat string, got %v", dst2["boat"])
}
if dst2["hole"].(string) != "black" {
t.Errorf("Expected hole string, got %v", dst2["boat"])
}
if dst2["nilval"] != nil {
t.Error("Expected nilvalue to have nil value but it does not")
}
require.True(t, ok, "Address went away.")
assert.Equal(t, "123 Spouter Inn Ct.", addr2["street"].(string), "Unexpected address: %v", addr2["street"])
assert.Equal(t, "Nantucket", addr2["city"].(string), "Unexpected city: %v", addr2["city"])
assert.Equal(t, "US", addr2["country"].(string), "Unexpected Country: %v", addr2["country"])
det2, ok := dst2["details"].(map[string]any)
require.Truef(t, ok, "Details is the wrong type: %v", dst2["details"])
assert.Contains(t, det2, "friends", "Could not find your friends. Maybe you don't have any. :-(")
assert.Equal(t, "pequod", dst2["boat"].(string), "Expected boat string, got %v", dst2["boat"])
assert.Equal(t, "black", dst2["hole"].(string), "Expected hole string, got %v", dst2["hole"])
assert.Nil(t, dst2["nilval"], "Expected nilvalue to have nil value but it does not")
}
func TestCoalesceValuesWarnings(t *testing.T) {
@ -716,9 +613,7 @@ func TestCoalesceValuesWarnings(t *testing.T) {
}
_, err := coalesce(printf, c, vals, "", false)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
t.Logf("vals: %v", vals)
assert.Contains(t, warnings, "warning: skipped value for level1.level2.level3.boat: Not a table.")
@ -761,8 +656,7 @@ func TestCoalesceValuesEmptyMapWithNils(t *testing.T) {
is.Equal("bar", data["foo"])
// "baz" should be preserved with nil value since it wasn't in chart defaults
_, ok = data["baz"]
is.True(ok, "Expected data.baz key to be present but it was removed")
is.Contains(data, "baz", "Expected data.baz key to be present but it was removed")
is.Nil(data["baz"], "Expected data.baz key to be nil but it is not")
}
@ -926,7 +820,6 @@ func TestCoalesceValuesSubchartNilCleanedWhenUserPartiallyOverrides(t *testing.T
keyMapping, ok := childVals["keyMapping"].(map[string]any)
is.True(ok, "keyMapping should be a map")
is.Equal("sha256", keyMapping["format"], "User override should be preserved")
_, ok = keyMapping["password"]

@ -20,75 +20,47 @@ import (
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/chart/common"
chart "helm.sh/helm/v4/pkg/chart/v2"
)
func TestValidateAgainstSingleSchema(t *testing.T) {
values, err := common.ReadValuesFile("./testdata/test-values.yaml")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
schema, err := os.ReadFile("./testdata/test-values.schema.json")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
require.NoError(t, err, "Error reading YAML file")
if err := ValidateAgainstSingleSchema(values, schema); err != nil {
t.Errorf("Error validating Values against Schema: %s", err)
}
schema, err := os.ReadFile("./testdata/test-values.schema.json")
require.NoError(t, err, "Error reading YAML file")
assert.NoErrorf(t, ValidateAgainstSingleSchema(values, schema), "Error validating Values against Schema")
}
func TestValidateAgainstInvalidSingleSchema(t *testing.T) {
values, err := common.ReadValuesFile("./testdata/test-values.yaml")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
schema, err := os.ReadFile("./testdata/test-values-invalid.schema.json")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
require.NoError(t, err, "Error reading YAML file")
var errString string
if err := ValidateAgainstSingleSchema(values, schema); err == nil {
t.Fatal("Expected an error, but got nil")
} else {
errString = err.Error()
}
schema, err := os.ReadFile("./testdata/test-values-invalid.schema.json")
require.NoError(t, err, "Error reading YAML file")
expectedErrString := `"file:///values.schema.json#" is not valid against metaschema: jsonschema validation failed with 'https://json-schema.org/draft/2020-12/schema#'
- at '': got number, want boolean or object`
if errString != expectedErrString {
t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString)
}
assert.EqualError(t, ValidateAgainstSingleSchema(values, schema), expectedErrString)
}
func TestValidateAgainstSingleSchemaNegative(t *testing.T) {
values, err := common.ReadValuesFile("./testdata/test-values-negative.yaml")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
schema, err := os.ReadFile("./testdata/test-values.schema.json")
if err != nil {
t.Fatalf("Error reading JSON file: %s", err)
}
require.NoError(t, err, "Error reading YAML file")
var errString string
if err := ValidateAgainstSingleSchema(values, schema); err == nil {
t.Fatal("Expected an error, but got nil")
} else {
errString = err.Error()
}
schema, err := os.ReadFile("./testdata/test-values.schema.json")
require.NoError(t, err, "Error reading JSON file")
expectedErrString := `- at '': missing property 'employmentInfo'
- at '/age': minimum: got -5, want 0
`
if errString != expectedErrString {
t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString)
}
assert.EqualError(t, ValidateAgainstSingleSchema(values, schema), expectedErrString)
}
const subchartSchema = `{
@ -145,9 +117,7 @@ func TestValidateAgainstSchema(t *testing.T) {
},
}
if err := ValidateAgainstSchema(chrt, vals); err != nil {
t.Errorf("Error validating Values against Schema: %s", err)
}
assert.NoErrorf(t, ValidateAgainstSchema(chrt, vals), "Error validating Values against Schema")
}
func TestValidateAgainstSchemaNegative(t *testing.T) {
@ -170,19 +140,10 @@ func TestValidateAgainstSchemaNegative(t *testing.T) {
"subchart": map[string]any{},
}
var errString string
if err := ValidateAgainstSchema(chrt, vals); err == nil {
t.Fatal("Expected an error, but got nil")
} else {
errString = err.Error()
}
expectedErrString := `subchart:
- at '': missing property 'age'
`
if errString != expectedErrString {
t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString)
}
assert.EqualError(t, ValidateAgainstSchema(chrt, vals), expectedErrString)
}
func TestValidateAgainstSchema2020(t *testing.T) {
@ -207,9 +168,7 @@ func TestValidateAgainstSchema2020(t *testing.T) {
},
}
if err := ValidateAgainstSchema(chrt, vals); err != nil {
t.Errorf("Error validating Values against Schema: %s", err)
}
assert.NoErrorf(t, ValidateAgainstSchema(chrt, vals), "Error validating Values against Schema")
}
func TestValidateAgainstSchema2020Negative(t *testing.T) {
@ -234,20 +193,11 @@ func TestValidateAgainstSchema2020Negative(t *testing.T) {
},
}
var errString string
if err := ValidateAgainstSchema(chrt, vals); err == nil {
t.Fatal("Expected an error, but got nil")
} else {
errString = err.Error()
}
expectedErrString := `subchart:
- at '/data': no items match contains schema
- at '/data/0': got number, want string
`
if errString != expectedErrString {
t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString)
}
assert.EqualError(t, ValidateAgainstSchema(chrt, vals), expectedErrString)
}
func TestHTTPURLLoader_Load(t *testing.T) {
@ -262,12 +212,8 @@ func TestHTTPURLLoader_Load(t *testing.T) {
loader := newHTTPURLLoader()
result, err := loader.Load(server.URL)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result == nil {
t.Fatal("Expected result to be non-nil")
}
require.NoError(t, err, "Expected no error, got")
require.NotNil(t, result, "Expected result to be non-nil")
})
t.Run("HTTP error status", func(t *testing.T) {
@ -278,12 +224,8 @@ func TestHTTPURLLoader_Load(t *testing.T) {
loader := newHTTPURLLoader()
_, err := loader.Load(server.URL)
if err == nil {
t.Fatal("Expected error for HTTP 404")
}
if !strings.Contains(err.Error(), "404") {
t.Errorf("Expected error message to contain '404', got: %v", err)
}
require.Error(t, err, "Expected error for HTTP 404")
assert.ErrorContains(t, err, "404", "Expected error message to contain '404'")
})
}
@ -295,9 +237,7 @@ func TestValidateAgainstSingleSchema_UnresolvedURN_Ignored(t *testing.T) {
"$ref": "urn:example:helm:schemas:v1:helm-schema-validation-conditions:v1/helmSchemaValidation-true"
}`)
vals := map[string]any{"any": "value"}
if err := ValidateAgainstSingleSchema(vals, schema); err != nil {
t.Fatalf("expected no error when URN unresolved is ignored, got: %v", err)
}
require.NoErrorf(t, ValidateAgainstSingleSchema(vals, schema), "expected no error when URN unresolved is ignored, got")
}
// Non-regression tests for https://github.com/helm/helm/issues/31202
@ -323,14 +263,10 @@ func TestValidateAgainstSchema_MissingSubchartValues_NoPanic(t *testing.T) {
}
defer func() {
if r := recover(); r != nil {
t.Fatalf("ValidateAgainstSchema panicked (missing subchart values): %v", r)
}
require.Nilf(t, recover(), "ValidateAgainstSchema panicked (missing subchart values)")
}()
if err := ValidateAgainstSchema(chrt, vals); err != nil {
t.Fatalf("expected no error when subchart values are missing, got: %v", err)
}
require.NoErrorf(t, ValidateAgainstSchema(chrt, vals), "expected no error when subchart values are missing, got")
}
func TestValidateAgainstSchema_SubchartNil_NoPanic(t *testing.T) {
@ -351,14 +287,10 @@ func TestValidateAgainstSchema_SubchartNil_NoPanic(t *testing.T) {
}
defer func() {
if r := recover(); r != nil {
t.Fatalf("ValidateAgainstSchema panicked (nil subchart values): %v", r)
}
require.Nilf(t, recover(), "ValidateAgainstSchema panicked (nil subchart values)")
}()
if err := ValidateAgainstSchema(chrt, vals); err != nil {
t.Fatalf("expected no error when subchart values are nil, got: %v", err)
}
require.NoErrorf(t, ValidateAgainstSchema(chrt, vals), "expected no error when subchart values are nil, got")
}
func TestValidateAgainstSchema_InvalidSubchartValuesType_NoPanic(t *testing.T) {
@ -379,13 +311,9 @@ func TestValidateAgainstSchema_InvalidSubchartValuesType_NoPanic(t *testing.T) {
}
defer func() {
if r := recover(); r != nil {
t.Fatalf("ValidateAgainstSchema panicked (invalid subchart values type): %v", r)
}
require.Nilf(t, recover(), "ValidateAgainstSchema panicked (invalid subchart values type)")
}()
// We expect a non-nil error (invalid type), but crucially no panic.
if err := ValidateAgainstSchema(chrt, vals); err == nil {
t.Fatal("expected an error when subchart values have invalid type, got nil")
}
require.Error(t, ValidateAgainstSchema(chrt, vals), "expected an error when subchart values have invalid type, got nil")
}

@ -20,6 +20,9 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/chart/common"
chart "helm.sh/helm/v4/pkg/chart/v2"
)
@ -61,42 +64,26 @@ func TestToRenderValues(t *testing.T) {
}
res, err := ToRenderValuesWithSchemaValidation(c, overrideValues, o, nil, false)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
// Ensure that the top-level values are all set.
metamap := res["Chart"].(map[string]any)
if name := metamap["Name"]; name.(string) != "test" {
t.Errorf("Expected chart name 'test', got %q", name)
}
name := metamap["Name"]
assert.Equalf(t, "test", name.(string), "Expected chart name 'test', got %q", name)
relmap := res["Release"].(map[string]any)
if name := relmap["Name"]; name.(string) != "Seven Voyages" {
t.Errorf("Expected release name 'Seven Voyages', got %q", name)
}
if namespace := relmap["Namespace"]; namespace.(string) != "default" {
t.Errorf("Expected namespace 'default', got %q", namespace)
}
if revision := relmap["Revision"]; revision.(int) != 1 {
t.Errorf("Expected revision '1', got %d", revision)
}
if relmap["IsUpgrade"].(bool) {
t.Error("Expected upgrade to be false.")
}
if !relmap["IsInstall"].(bool) {
t.Error("Expected install to be true.")
}
if !res["Capabilities"].(*common.Capabilities).APIVersions.Has("v1") {
t.Error("Expected Capabilities to have v1 as an API")
}
if res["Capabilities"].(*common.Capabilities).KubeVersion.Major != "1" {
t.Error("Expected Capabilities to have a Kube version")
}
name = relmap["Name"]
assert.Equalf(t, "Seven Voyages", name.(string), "Expected release name 'Seven Voyages', got %q", name)
namespace := relmap["Namespace"]
assert.Equalf(t, "default", namespace.(string), "Expected namespace 'default', got %q", namespace)
revision := relmap["Revision"]
assert.Equalf(t, 1, revision.(int), "Expected revision '1', got %d", revision)
assert.False(t, relmap["IsUpgrade"].(bool), "Expected upgrade to be false.")
assert.True(t, relmap["IsInstall"].(bool), "Expected install to be true.")
assert.True(t, res["Capabilities"].(*common.Capabilities).APIVersions.Has("v1"), "Expected Capabilities to have v1 as an API")
assert.Equal(t, "1", res["Capabilities"].(*common.Capabilities).KubeVersion.Major, "Expected Capabilities to have a Kube version")
vals := res["Values"].(common.Values)
if vals["name"] != "Haroun" {
t.Errorf("Expected 'Haroun', got %q (%v)", vals["name"], vals)
}
assert.Equal(t, "Haroun", vals["name"], "Expected 'Haroun', got %q (%v)", vals["name"], vals)
where := vals["where"].(map[string]any)
expects := map[string]string{
"city": "Baghdad",
@ -104,8 +91,7 @@ func TestToRenderValues(t *testing.T) {
"title": "caliph",
}
for field, expect := range expects {
if got := where[field]; got != expect {
t.Errorf("Expected %q, got %q (%v)", expect, got, where)
}
got := where[field]
assert.Equalf(t, got, expect, "Expected %q, got %q (%v)", expect, got, where)
}
}

@ -21,6 +21,9 @@ import (
"fmt"
"testing"
"text/template"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestReadValues(t *testing.T) {
@ -46,29 +49,21 @@ water:
`
data, err := ReadValues([]byte(doc))
if err != nil {
t.Fatalf("Error parsing bytes: %s", err)
}
require.NoErrorf(t, err, "Error parsing bytes")
matchValues(t, data)
tests := []string{`poet: "Coleridge"`, "# Just a comment", ""}
for _, tt := range tests {
data, err = ReadValues([]byte(tt))
if err != nil {
t.Fatalf("Error parsing bytes (%s): %s", tt, err)
}
if data == nil {
t.Errorf(`YAML string "%s" gave a nil map`, tt)
}
require.NoErrorf(t, err, "Error parsing bytes (%s)", tt)
require.NotNilf(t, data, `YAML string "%s" gave a nil map`, tt)
}
}
func TestReadValuesFile(t *testing.T) {
data, err := ReadValuesFile("./testdata/coleridge.yaml")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
require.NoErrorf(t, err, "Error reading YAML file")
matchValues(t, data)
}
@ -108,56 +103,40 @@ chapter:
title: "The Spouter Inn"
`
d, err := ReadValues([]byte(doc))
if err != nil {
t.Fatalf("Failed to parse the White Whale: %s", err)
}
require.NoErrorf(t, err, "Failed to parse the White Whale")
if _, err := d.Table("title"); err == nil {
t.Fatal("Title is not a table.")
}
_, err = d.Table("title")
require.Error(t, err, "Title is not a table.")
if _, err := d.Table("chapter"); err != nil {
t.Fatalf("Failed to get the chapter table: %s\n%v", err, d)
}
_, err = d.Table("chapter")
require.NoErrorf(t, err, "Failed to get the chapter table: %v", d)
if v, err := d.Table("chapter.one"); err != nil {
t.Errorf("Failed to get chapter.one: %s", err)
} else if v["title"] != "Loomings" {
t.Errorf("Unexpected title: %s", v["title"])
}
v, err := d.Table("chapter.one")
require.NoErrorf(t, err, "Failed to get chapter.one")
assert.Equalf(t, "Loomings", v["title"], "Unexpected title: %s", v["title"])
if _, err := d.Table("chapter.three"); err != nil {
t.Errorf("Chapter three is missing: %s\n%v", err, d)
}
_, err = d.Table("chapter.three")
require.NoErrorf(t, err, "Chapter three is missing: %v", d)
if _, err := d.Table("chapter.OneHundredThirtySix"); err == nil {
t.Error("I think you mean 'Epilogue'")
}
_, err = d.Table("chapter.OneHundredThirtySix")
assert.Error(t, err, "I think you mean 'Epilogue'")
}
func matchValues(t *testing.T, data map[string]any) {
t.Helper()
if data["poet"] != "Coleridge" {
t.Errorf("Unexpected poet: %s", data["poet"])
}
assert.Equalf(t, "Coleridge", data["poet"], "Unexpected poet: %s", data["poet"])
if o, err := ttpl("{{len .stanza}}", data); err != nil {
t.Errorf("len stanza: %s", err)
} else if o != "6" {
t.Errorf("Expected 6, got %s", o)
}
o, err := ttpl("{{len .stanza}}", data)
require.NoErrorf(t, err, "len stanza")
assert.Equalf(t, "6", o, "Expected 6, got %s", o)
if o, err := ttpl("{{.mariner.shot}}", data); err != nil {
t.Errorf(".mariner.shot: %s", err)
} else if o != "ALBATROSS" {
t.Error("Expected that mariner shot ALBATROSS")
}
o, err = ttpl("{{.mariner.shot}}", data)
require.NoErrorf(t, err, ".mariner.shot")
assert.Equal(t, "ALBATROSS", o, "Expected that mariner shot ALBATROSS")
if o, err := ttpl("{{.water.water.where}}", data); err != nil {
t.Errorf(".water.water.where: %s", err)
} else if o != "everywhere" {
t.Error("Expected water water everywhere")
}
o, err = ttpl("{{.water.water.where}}", data)
require.NoErrorf(t, err, ".water.water.where")
assert.Equal(t, "everywhere", o, "Expected water water everywhere")
}
func ttpl(tpl string, v map[string]any) (string, error) {
@ -179,27 +158,18 @@ chapter:
title: "The Spouter Inn"
`
d, err := ReadValues([]byte(doc))
if err != nil {
t.Fatalf("Failed to parse the White Whale: %s", err)
}
if v, err := d.PathValue("chapter.one.title"); err != nil {
t.Errorf("Got error instead of title: %s\n%v", err, d)
} else if v != "Loomings" {
t.Errorf("No error but got wrong value for title: %s\n%v", err, d)
}
if _, err := d.PathValue("chapter.one.doesnotexist"); err == nil {
t.Errorf("Non-existent key should return error: %s\n%v", err, d)
}
if _, err := d.PathValue("chapter.doesnotexist.one"); err == nil {
t.Errorf("Non-existent key in middle of path should return error: %s\n%v", err, d)
}
if _, err := d.PathValue(""); err == nil {
t.Error("Asking for the value from an empty path should yield an error")
}
if v, err := d.PathValue("title"); err == nil {
if v != "Moby Dick" {
t.Error("Failed to return values for root key title")
}
}
require.NoErrorf(t, err, "Failed to parse the White Whale")
v, err := d.PathValue("chapter.one.title")
require.NoErrorf(t, err, "Got error instead of title: %v", d)
assert.Equalf(t, "Loomings", v, "No error but got wrong value for title: %v", d)
_, err = d.PathValue("chapter.one.doesnotexist")
require.Errorf(t, err, "Non-existent key should return error: %v", d)
_, err = d.PathValue("chapter.doesnotexist.one")
require.Errorf(t, err, "Non-existent key in middle of path should return error: %v", d)
_, err = d.PathValue("")
require.Error(t, err, "Asking for the value from an empty path should yield an error")
v, err = d.PathValue("title")
require.NoErrorf(t, err, "Failed to get title: %v", d)
assert.Equalf(t, "Moby Dick", v, "Failed to return values for root key title: got %s\n%v", v, d)
}

@ -21,6 +21,8 @@ import (
"bytes"
"compress/gzip"
"testing"
"github.com/stretchr/testify/require"
)
func TestLoadArchiveFiles(t *testing.T) {
@ -34,9 +36,7 @@ func TestLoadArchiveFiles(t *testing.T) {
generate: func(_ *tar.Writer) {},
check: func(t *testing.T, _ []*BufferedFile, err error) {
t.Helper()
if err.Error() != "no files in chart archive" {
t.Fatalf(`expected "no files in chart archive", got [%#v]`, err)
}
require.EqualError(t, err, "no files in chart archive")
},
},
{
@ -44,32 +44,21 @@ func TestLoadArchiveFiles(t *testing.T) {
generate: func(w *tar.Writer) {
// simulate the presence of a `pax_global_header` file like you would get when
// processing a GitHub release archive.
err := w.WriteHeader(&tar.Header{
require.NoError(t, w.WriteHeader(&tar.Header{
Typeflag: tar.TypeXGlobalHeader,
Name: "pax_global_header",
})
if err != nil {
t.Fatal(err)
}
}))
// we need to have at least one file, otherwise we'll get the "no files in chart archive" error
err = w.WriteHeader(&tar.Header{
require.NoError(t, w.WriteHeader(&tar.Header{
Typeflag: tar.TypeReg,
Name: "dir/empty",
})
if err != nil {
t.Fatal(err)
}
}))
},
check: func(t *testing.T, files []*BufferedFile, err error) {
t.Helper()
if err != nil {
t.Fatalf(`got unwanted error [%#v] for tar file with pax_global_header content`, err)
}
if len(files) != 1 {
t.Fatalf(`expected to get one file but got [%v]`, files)
}
require.NoErrorf(t, err, `got unwanted error for tar file with pax_global_header content`)
require.Lenf(t, files, 1, `expected to get one file but got [%v]`, files)
},
},
}

@ -24,10 +24,12 @@ import (
"io"
"maps"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
c3 "helm.sh/helm/v4/internal/chart/v3"
"helm.sh/helm/v4/pkg/chart"
c2 "helm.sh/helm/v4/pkg/chart/v2"
@ -59,20 +61,13 @@ description: A test chart
Size: int64(len(data)),
ModTime: time.Now(),
}
if err := tw.WriteHeader(header); err != nil {
t.Fatalf("Failed to write tar header for %s: %v", name, err)
}
if _, err := tw.Write(data); err != nil {
t.Fatalf("Failed to write tar data for %s: %v", name, err)
}
require.NoErrorf(t, tw.WriteHeader(header), "Failed to write tar header for %s", name)
_, err := tw.Write(data)
require.NoErrorf(t, err, "Failed to write tar data for %s", name)
}
if err := tw.Close(); err != nil {
t.Fatalf("Failed to close tar writer: %v", err)
}
if err := gw.Close(); err != nil {
t.Fatalf("Failed to close gzip writer: %v", err)
}
require.NoErrorf(t, tw.Close(), "Failed to close tar writer")
require.NoErrorf(t, gw.Close(), "Failed to close gzip writer")
return &buf
}
@ -149,37 +144,26 @@ func TestLoadArchive(t *testing.T) {
loadedChart, err := LoadArchive(reader)
if tc.expectedError != "" {
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
t.Errorf("Expected error containing %q, but got %v", tc.expectedError, err)
}
return
}
require.Errorf(t, err, "Expected error containing %q, but got no error", tc.expectedError)
assert.ErrorContains(t, err, tc.expectedError)
} else {
require.NoError(t, err)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
lac, err := chart.NewAccessor(loadedChart)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
eac, err := chart.NewAccessor(tc.expectedChart)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
lac, err := chart.NewAccessor(loadedChart)
require.NoError(t, err)
if lac.Name() != eac.Name() {
t.Errorf("Expected chart name %q, got %q", eac.Name(), lac.Name())
}
eac, err := chart.NewAccessor(tc.expectedChart)
require.NoError(t, err)
assert.Equalf(t, lac.Name(), eac.Name(), "Expected chart name %q, got %q", eac.Name(), lac.Name())
var loadedAPIVersion string
switch lc := loadedChart.(type) {
case *c2.Chart:
loadedAPIVersion = lc.Metadata.APIVersion
case *c3.Chart:
loadedAPIVersion = lc.Metadata.APIVersion
}
if loadedAPIVersion != tc.apiVersion {
t.Errorf("Expected API version %q, got %q", tc.apiVersion, loadedAPIVersion)
var loadedAPIVersion string
switch lc := loadedChart.(type) {
case *c2.Chart:
loadedAPIVersion = lc.Metadata.APIVersion
case *c3.Chart:
loadedAPIVersion = lc.Metadata.APIVersion
}
assert.Equalf(t, loadedAPIVersion, tc.apiVersion, "Expected API version %q, got %q", tc.apiVersion, loadedAPIVersion)
}
})
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save