55 "encoding/json"
66 "fmt"
77 "io"
8+ "net/http"
9+ "net/http/httptest"
810 "os"
911 "os/exec"
1012 "path/filepath"
@@ -22,6 +24,7 @@ import (
2224 "ocm.software/open-component-model/bindings/go/blob/compression"
2325 "ocm.software/open-component-model/bindings/go/blob/direct"
2426 "ocm.software/open-component-model/bindings/go/blob/filesystem"
27+ helmblob "ocm.software/open-component-model/bindings/go/helm/blob"
2528 descriptor "ocm.software/open-component-model/bindings/go/descriptor/runtime"
2629 v2 "ocm.software/open-component-model/bindings/go/descriptor/v2"
2730 "ocm.software/open-component-model/bindings/go/oci"
@@ -420,6 +423,114 @@ func Test_Integration_HelmTransformer(t *testing.T) {
420423 })
421424}
422425
426+ // Test_Integration_DownloadResource_HelmAccess verifies that a resource with helm access
427+ // can be downloaded from a CTF. The download triggers the Helm ResourceRepository to
428+ // fetch the chart from the remote helm repository and return it as a blob.
429+ func Test_Integration_DownloadResource_HelmAccess (t * testing.T ) {
430+ t .Parallel ()
431+ r := require .New (t )
432+ ctx := t .Context ()
433+
434+ root := getRepoRootBasedOnGit (t )
435+ chartDir := filepath .Join (root , "bindings/go/helm/testdata/provenance" )
436+
437+ srv := httptest .NewServer (http .FileServer (http .Dir (chartDir )))
438+ t .Cleanup (srv .Close )
439+
440+ componentName := "ocm.software/helm-access-download"
441+ componentVersion := "v1.0.0"
442+
443+ constructor := fmt .Sprintf (`components:
444+ - name: %s
445+ version: %s
446+ provider:
447+ name: ocm.software
448+ resources:
449+ - name: mychart
450+ version: 0.1.0
451+ type: helmChart
452+ access:
453+ type: helm/v1
454+ helmRepository: %s
455+ helmChart: mychart-0.1.0.tgz
456+ ` , componentName , componentVersion , srv .URL )
457+
458+ tempDir := t .TempDir ()
459+ constructorPath := filepath .Join (tempDir , "constructor.yaml" )
460+ r .NoError (os .WriteFile (constructorPath , []byte (constructor ), os .ModePerm ))
461+
462+ ctfDir := filepath .Join (tempDir , "ctf" )
463+
464+ addCMD := cmd .New ()
465+ addCMD .SetArgs ([]string {
466+ "add" ,
467+ "component-version" ,
468+ "--repository" , fmt .Sprintf ("ctf::%s" , ctfDir ),
469+ "--constructor" , constructorPath ,
470+ "--skip-reference-digest-processing" ,
471+ })
472+ r .NoError (addCMD .ExecuteContext (ctx ), "add component-version should succeed" )
473+
474+ output := filepath .Join (t .TempDir (), "downloaded-chart" )
475+ downloadCMD := cmd .New ()
476+ downloadCMD .SetArgs ([]string {
477+ "download" ,
478+ "resource" ,
479+ fmt .Sprintf ("ctf::%s//%s:%s" , ctfDir , componentName , componentVersion ),
480+ "--identity" , "name=mychart,version=0.1.0" ,
481+ "--output" , output ,
482+ "--extraction-policy" , "disable" ,
483+ })
484+ r .NoError (downloadCMD .ExecuteContext (ctx ), "download resource with helm access should succeed" )
485+
486+ // The ResourceRepository returns a tar archive containing the chart .tgz and .prov files.
487+ chartPath := filepath .Join (chartDir , "mychart-0.1.0.tgz" )
488+ provPath := filepath .Join (chartDir , "mychart-0.1.0.tgz.prov" )
489+ assertHelmChartTar (t , output , chartPath , provPath )
490+ }
491+
492+ // assertHelmChartTar verifies that a tar file contains the expected chart and
493+ // optional provenance file by comparing their contents byte-for-byte against
494+ // the originals. This is useful for verifying raw downloads from the Helm
495+ // ResourceRepository, which returns a tar archive (not an OCI layout).
496+ func assertHelmChartTar (t * testing.T , tarPath , originalChartPath , originalProvPath string ) {
497+ t .Helper ()
498+ r := require .New (t )
499+
500+ expectedChart , err := os .ReadFile (originalChartPath )
501+ r .NoError (err , "should read original chart" )
502+
503+ tarBlob , err := filesystem .GetBlobFromOSPath (tarPath )
504+ r .NoError (err , "should open downloaded tar" )
505+
506+ chartBlob := helmblob .NewChartBlob (tarBlob )
507+
508+ chartArchive , err := chartBlob .ChartArchive ()
509+ r .NoError (err , "tar should contain the chart .tgz" )
510+ r .Equal (expectedChart , readAllFromBlob (t , chartArchive ), "downloaded chart should match the original" )
511+
512+ provFile , err := chartBlob .ProvFile ()
513+ r .NoError (err , "reading prov file from chart blob should succeed" )
514+ if originalProvPath != "" {
515+ r .NotNil (provFile , "tar should contain the .prov file" )
516+ expectedProv , err := os .ReadFile (originalProvPath )
517+ r .NoError (err , "should read original prov file" )
518+ r .Equal (expectedProv , readAllFromBlob (t , provFile ), "downloaded prov file should match the original" )
519+ }
520+ }
521+
522+ // readAllFromBlob reads the full content of a read-only blob, failing the test on error.
523+ func readAllFromBlob (t * testing.T , b blob.ReadOnlyBlob ) []byte {
524+ t .Helper ()
525+ r := require .New (t )
526+ rc , err := b .ReadCloser ()
527+ r .NoError (err , "should open blob reader" )
528+ defer func () { _ = rc .Close () }()
529+ data , err := io .ReadAll (rc )
530+ r .NoError (err , "should read blob content" )
531+ return data
532+ }
533+
423534func Test_Integration_ConstructorCompress (t * testing.T ) {
424535 originalContent := "This is the original file content for compress test."
425536 name , version := "ocm.software/compress-test" , "v1.0.0"
0 commit comments