@@ -57,7 +57,7 @@ func TestGenerate_Success(t *testing.T) {
5757 }
5858
5959 // Verify root files exist
60- rootFiles := []string {"README.md" , "deploy.sh" }
60+ rootFiles := []string {"README.md" , "deploy.sh" , "undeploy.sh" }
6161 for _ , f := range rootFiles {
6262 path := filepath .Join (outputDir , f )
6363 if _ , statErr := os .Stat (path ); os .IsNotExist (statErr ) {
@@ -101,9 +101,9 @@ func TestGenerate_Success(t *testing.T) {
101101 t .Error ("Chart.yaml should not exist in per-component bundle" )
102102 }
103103
104- // Verify output has reasonable file count (2 root files + 2 component dirs × 2 files each = 6 )
105- if len (output .Files ) < 6 {
106- t .Errorf ("expected at least 6 files, got %d" , len (output .Files ))
104+ // Verify output has reasonable file count (3 root files + 2 component dirs × 2 files each = 7 )
105+ if len (output .Files ) < 7 {
106+ t .Errorf ("expected at least 7 files, got %d" , len (output .Files ))
107107 }
108108}
109109
@@ -187,6 +187,9 @@ func TestGenerate_WithChecksums(t *testing.T) {
187187 if ! strings .Contains (content , "deploy.sh" ) {
188188 t .Error ("checksums.txt missing deploy.sh" )
189189 }
190+ if ! strings .Contains (content , "undeploy.sh" ) {
191+ t .Error ("checksums.txt missing undeploy.sh" )
192+ }
190193 if ! strings .Contains (content , filepath .Join ("cert-manager" , "values.yaml" )) {
191194 t .Error ("checksums.txt missing cert-manager/values.yaml" )
192195 }
@@ -382,6 +385,65 @@ func TestGenerate_DeployScriptExecutable(t *testing.T) {
382385 }
383386}
384387
388+ func TestGenerate_UndeployScriptExecutable (t * testing.T ) {
389+ g := NewGenerator ()
390+ ctx := context .Background ()
391+ outputDir := t .TempDir ()
392+
393+ input := & GeneratorInput {
394+ RecipeResult : createTestRecipeResult (),
395+ ComponentValues : map [string ]map [string ]any {
396+ "cert-manager" : {},
397+ "gpu-operator" : {},
398+ },
399+ Version : "v1.0.0" ,
400+ }
401+
402+ _ , err := g .Generate (ctx , input , outputDir )
403+ if err != nil {
404+ t .Fatalf ("Generate failed: %v" , err )
405+ }
406+
407+ undeployPath := filepath .Join (outputDir , "undeploy.sh" )
408+ info , statErr := os .Stat (undeployPath )
409+ if os .IsNotExist (statErr ) {
410+ t .Fatal ("undeploy.sh does not exist" )
411+ }
412+
413+ // Check executable permission (0755)
414+ mode := info .Mode ()
415+ if mode & 0111 == 0 {
416+ t .Errorf ("undeploy.sh is not executable, mode: %o" , mode )
417+ }
418+
419+ // Verify content
420+ content , err := os .ReadFile (undeployPath )
421+ if err != nil {
422+ t .Fatalf ("failed to read undeploy.sh: %v" , err )
423+ }
424+ script := string (content )
425+
426+ if ! strings .HasPrefix (script , "#!/usr/bin/env bash" ) {
427+ t .Error ("undeploy.sh missing shebang" )
428+ }
429+ if ! strings .Contains (script , "set -euo pipefail" ) {
430+ t .Error ("undeploy.sh missing strict mode" )
431+ }
432+ if ! strings .Contains (script , "helm uninstall" ) {
433+ t .Error ("undeploy.sh missing helm uninstall command" )
434+ }
435+
436+ // Verify reverse order: gpu-operator should appear before cert-manager
437+ gpuIdx := strings .Index (script , "Uninstalling gpu-operator" )
438+ certIdx := strings .Index (script , "Uninstalling cert-manager" )
439+ if gpuIdx < 0 || certIdx < 0 {
440+ t .Fatal ("undeploy.sh missing component uninstall lines" )
441+ }
442+ if gpuIdx > certIdx {
443+ t .Error ("undeploy.sh components not in reverse order: gpu-operator should come before cert-manager" )
444+ }
445+ }
446+
385447func TestNormalizeVersion (t * testing.T ) {
386448 tests := []struct {
387449 input string
0 commit comments