Skip to content

Commit 2f465d6

Browse files
authored
Integrate azd-core v0.2.0 and Extract Testing Utilities (#26)
* refactor: rename AZD_SCRIPT_DEBUG to AZD_DEBUG and update related documentation * chore: update dependencies and add new changelog entries - Updated astro from 5.16.6 to 5.16.8 - Updated @playwright/test from 1.49.0 to 1.57.0 - Updated @types/node from 22.10.2 to 25.0.5 - Updated tailwindcss from 4.0.0 to 4.1.18 - Updated tsx from 4.19.2 to 4.21.0 - Updated typescript from 5.7.2 to 5.9.3 - Added new release entries for versions 0.2.30 and 0.2.29 in the changelog * feat: extract testhelpers to azd-core/testutil package - Created azd-core/testutil package with enhanced functionality. - Extracted CaptureOutput, FindTestData, TempDir, and Contains helpers. - Added comprehensive tests targeting ≥85% coverage. - Updated azd-core README with testutil documentation. feat: extract cliout package to azd-core for standardized CLI output - Created azd-core/cliout package with full output formatting functions. - Maintained Unicode detection, ANSI colors, and JSON mode. - Added tests targeting ≥80% coverage. - Updated azd-core README with cliout documentation. refactor: migrate azd-exec to use azd-core/testutil - Updated azd-exec to replace internal testhelpers with testutil. - Deleted internal testhelpers package. - Verified all tests pass with no regressions. refactor: migrate azd-app to use azd-core/testutil - Updated azd-app test infrastructure to utilize testutil. - Enhanced existing tests with CaptureOutput and Contains. - Verified all tests pass. refactor: migrate azd-app output to azd-core/cliout - Updated azd-app to import cliout from azd-core. - Deleted internal output package. - Verified CLI output remains unchanged. feat: enhance azd-exec CLI output using cliout - Updated azd-exec commands to use cliout for consistent output formatting. - Added JSON output mode support. - Verified all tests pass. docs: create extension patterns guide in azd-core - Documented version management, logging, and structure patterns. - Included examples from azd-exec and azd-app. chore: update azd-core v0.3.0 release notes - Documented new packages (testutil, cliout) and integration impact. - Updated CHANGELOG.md with v0.3.0 entry. * chore: update azd-core target version to v0.2.0 in extraction specs and tasks * fix: handle errors when setting output format in version command * fix: use azd-core v0.2.1 and remove local replace directive for CI
1 parent 7cceee0 commit 2f465d6

22 files changed

Lines changed: 1405 additions & 959 deletions

cli/docs/cli-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ All azd environment variables are available to your scripts:
212212
- Variables from `.azure/<env>/.env` file
213213

214214
**azd-exec Specific:**
215-
- `AZD_SCRIPT_DEBUG` - Set to "true" when `--debug` flag is used
215+
- `AZD_DEBUG` - Set to "true" when `--debug` flag is used
216216
- `AZD_NO_PROMPT` - Set to "true" when `--no-prompt` flag is used
217217

218218
### Azure Key Vault Integration

cli/docs/security-review.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ absPath, err := filepath.Abs(scriptPath) // Resolve to absolute path
8989
- ✅ Environment variables inherited from parent process (`os.Environ()`)
9090
- ✅ Azd context variables passed through safely
9191
- ✅ No sensitive data logged by default
92-
- ✅ Debug mode explicitly requires `AZD_SCRIPT_DEBUG=true`
92+
- ✅ Debug mode explicitly requires `AZD_DEBUG=true`
9393

9494
**Debug Output** (Optional, user-controlled):
9595
```go
9696
// executor.go
97-
if os.Getenv("AZD_SCRIPT_DEBUG") == "true" {
97+
if os.Getenv("AZD_DEBUG") == "true" {
9898
fmt.Fprintf(os.Stderr, "Executing: %s %s\n", shell, strings.Join(cmd.Args[1:], " "))
9999
fmt.Fprintf(os.Stderr, "Working directory: %s\n", workingDir)
100100
}

cli/go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ module github.com/jongio/azd-exec/cli
33
go 1.25.5
44

55
require (
6-
github.com/jongio/azd-core v0.1.1-0.20260109201524-89cce2a55fae
6+
github.com/jongio/azd-core v0.2.1
77
github.com/magefile/mage v1.15.0
8-
github.com/spf13/cobra v1.8.1
8+
github.com/spf13/cobra v1.10.2
99
)
1010

1111
require (
@@ -20,9 +20,9 @@ require (
2020
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2121
github.com/kylelemons/godebug v1.1.0 // indirect
2222
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
23-
github.com/spf13/pflag v1.0.5 // indirect
23+
github.com/spf13/pflag v1.0.10 // indirect
2424
golang.org/x/crypto v0.46.0 // indirect
2525
golang.org/x/net v0.48.0 // indirect
2626
golang.org/x/sys v0.40.0 // indirect
27-
golang.org/x/text v0.32.0 // indirect
27+
golang.org/x/text v0.33.0 // indirect
2828
)

cli/go.sum

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ
1414
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
1515
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
1616
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
17-
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
18-
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
19-
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17+
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
18+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
19+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2020
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
2121
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
2222
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
2323
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
2424
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
2525
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
26-
github.com/jongio/azd-core v0.1.1-0.20260109201524-89cce2a55fae h1:yLg4cfkgnNUvL5K7Ytnh2SHW2r9zoys85ESP2t+E/G4=
27-
github.com/jongio/azd-core v0.1.1-0.20260109201524-89cce2a55fae/go.mod h1:AJ0B6SuTUZkLdEeFpyfEhVRs6exoWzZz5cBL8gVgt/A=
26+
github.com/jongio/azd-core v0.2.1 h1:0xByH2Q3GOg5yQw3sRbwuOHsPKuDJcXIf3wTZ4foRJA=
27+
github.com/jongio/azd-core v0.2.1/go.mod h1:qHrhg2tFMs83tYRax0EC0Bl3EdRN1WDOhmQkKcNpIGs=
2828
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
2929
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
3030
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
@@ -33,24 +33,26 @@ github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
3333
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
3434
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
3535
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
36-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
37-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
36+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
37+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3838
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
39-
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
40-
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
41-
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
42-
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
39+
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
40+
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
41+
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
42+
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
43+
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
4344
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
4445
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
46+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
4547
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
4648
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
4749
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
4850
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
4951
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
5052
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
5153
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
52-
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
53-
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
54+
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
55+
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
5456
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5557
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
5658
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

cli/src/cmd/exec/commands/listen.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package commands
33

44
import (
5+
"github.com/jongio/azd-core/cliout"
56
"github.com/spf13/cobra"
67
)
78

@@ -21,6 +22,7 @@ func NewListenCommand() *cobra.Command {
2122
// In a full implementation, this would start a gRPC server for azd communication.
2223
// For now, this extension operates in "exec mode" without persistent listener,
2324
// so returning nil is appropriate.
25+
cliout.Info("Listen command is not implemented - azd-exec operates in exec mode")
2426
return nil
2527
},
2628
}

cli/src/cmd/exec/commands/version.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ package commands
44
import (
55
"encoding/json"
66
"fmt"
7-
"os"
87

8+
"github.com/jongio/azd-core/cliout"
99
"github.com/jongio/azd-exec/cli/src/internal/version"
1010
"github.com/spf13/cobra"
1111
)
@@ -21,22 +21,43 @@ func NewVersionCommand(outputFormat *string) *cobra.Command {
2121
Short: "Display the extension version",
2222
Long: `Display the version information for the azd exec extension.`,
2323
Run: func(cmd *cobra.Command, args []string) {
24-
switch *outputFormat {
25-
case "json":
24+
// Set output format from flag
25+
if *outputFormat == "json" {
26+
if err := cliout.SetFormat("json"); err != nil {
27+
cliout.Error("Failed to set output format: %v", err)
28+
return
29+
}
30+
} else {
31+
if err := cliout.SetFormat("default"); err != nil {
32+
cliout.Error("Failed to set output format: %v", err)
33+
return
34+
}
35+
}
36+
37+
if cliout.IsJSON() {
38+
// JSON output mode
2639
output := map[string]string{
2740
"version": version.Version,
2841
}
2942
data, err := json.MarshalIndent(output, "", " ")
3043
if err != nil {
31-
fmt.Fprintf(os.Stderr, "Error formatting JSON: %v\n", err)
44+
cliout.Error("Error formatting JSON: %v", err)
3245
return
3346
}
3447
fmt.Println(string(data))
35-
default:
48+
} else {
49+
// Human-readable output with colors
3650
if quiet {
3751
fmt.Println(version.Version)
3852
} else {
39-
fmt.Printf("azd exec version %s\n", version.Version)
53+
cliout.Header("azd exec")
54+
cliout.Label("Version", version.Version)
55+
if version.BuildDate != "unknown" {
56+
cliout.Label("Build Date", version.BuildDate)
57+
}
58+
if version.GitCommit != "unknown" {
59+
cliout.Label("Git Commit", version.GitCommit)
60+
}
4061
}
4162
}
4263
},

cli/src/cmd/exec/commands/version_integration_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"strings"
99
"testing"
1010

11-
"github.com/jongio/azd-exec/cli/src/internal/testhelpers"
11+
"github.com/jongio/azd-core/testutil"
1212
"github.com/jongio/azd-exec/cli/src/internal/version"
1313
)
1414

@@ -25,7 +25,7 @@ func TestVersionCommandIntegration(t *testing.T) {
2525
{
2626
name: "Default output",
2727
outputFlag: "default",
28-
wantText: "azd exec version",
28+
wantText: "azd exec",
2929
},
3030
{
3131
name: "JSON output",
@@ -50,7 +50,7 @@ func TestVersionCommandIntegration(t *testing.T) {
5050
}
5151

5252
// Capture output
53-
output := testhelpers.CaptureOutput(t, func() error {
53+
output := testutil.CaptureOutput(t, func() error {
5454
return cmd.Execute()
5555
})
5656

@@ -82,7 +82,7 @@ func TestVersionCommandIntegration_QuietFlag(t *testing.T) {
8282
cmd := NewVersionCommand(&outputFormat)
8383
cmd.Flags().Set("quiet", "true")
8484

85-
output := testhelpers.CaptureOutput(t, func() error {
85+
output := testutil.CaptureOutput(t, func() error {
8686
return cmd.Execute()
8787
})
8888

@@ -106,7 +106,7 @@ func TestVersionCommandIntegration_JSONFormat(t *testing.T) {
106106
outputFormat := "json"
107107
cmd := NewVersionCommand(&outputFormat)
108108

109-
output := testhelpers.CaptureOutput(t, func() error {
109+
output := testutil.CaptureOutput(t, func() error {
110110
return cmd.Execute()
111111
})
112112

cli/src/cmd/exec/main.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path/filepath"
1111

12+
"github.com/jongio/azd-core/cliout"
1213
"github.com/jongio/azd-exec/cli/src/cmd/exec/commands"
1314
"github.com/jongio/azd-exec/cli/src/internal/executor"
1415
"github.com/spf13/cobra"
@@ -48,7 +49,7 @@ var newScriptExecutor = func(config executor.Config) scriptExecutor {
4849
func main() {
4950
rootCmd := newRootCmd()
5051
if err := rootCmd.Execute(); err != nil {
51-
fmt.Fprintln(os.Stderr, err)
52+
cliout.Error("%v", err)
5253
os.Exit(1)
5354
}
5455
}
@@ -100,6 +101,13 @@ Examples:
100101
return exec.ExecuteInline(cmd.Context(), scriptInput)
101102
},
102103
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
104+
// Set output format from flag
105+
if outputFormat == "json" {
106+
if err := cliout.SetFormat("json"); err != nil {
107+
return fmt.Errorf("failed to set output format: %w", err)
108+
}
109+
}
110+
103111
// Handle working directory change
104112
if cwd != "" {
105113
if err := os.Chdir(cwd); err != nil {
@@ -109,7 +117,7 @@ Examples:
109117

110118
// Handle debug mode
111119
if debugMode {
112-
_ = os.Setenv("AZD_SCRIPT_DEBUG", "true")
120+
_ = os.Setenv("AZD_DEBUG", "true")
113121
}
114122

115123
// Handle no-prompt mode

cli/src/cmd/exec/main_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestPersistentPreRunE_SetsEnvAndCwd(t *testing.T) {
3333

3434
newWd := t.TempDir()
3535

36-
oldDebug := os.Getenv("AZD_SCRIPT_DEBUG")
36+
oldDebug := os.Getenv("AZD_DEBUG")
3737
oldNoPrompt := os.Getenv("AZD_NO_PROMPT")
3838
oldEnvName := os.Getenv("AZURE_ENV_NAME")
3939
oldTraceFile := os.Getenv("AZD_TRACE_LOG_FILE")
@@ -83,8 +83,8 @@ func TestPersistentPreRunE_SetsEnvAndCwd(t *testing.T) {
8383
t.Fatalf("expected cwd %q, got %q", newWdNorm, gotWdNorm)
8484
}
8585

86-
if os.Getenv("AZD_SCRIPT_DEBUG") != "true" {
87-
t.Fatalf("expected AZD_SCRIPT_DEBUG=true")
86+
if os.Getenv("AZD_DEBUG") != "true" {
87+
t.Fatalf("expected AZD_DEBUG=true")
8888
}
8989
if os.Getenv("AZD_NO_PROMPT") != "true" {
9090
t.Fatalf("expected AZD_NO_PROMPT=true")
@@ -101,7 +101,7 @@ func TestPersistentPreRunE_SetsEnvAndCwd(t *testing.T) {
101101

102102
// Restore process state.
103103
_ = os.Chdir(oldWd)
104-
_ = os.Setenv("AZD_SCRIPT_DEBUG", oldDebug)
104+
_ = os.Setenv("AZD_DEBUG", oldDebug)
105105
_ = os.Setenv("AZD_NO_PROMPT", oldNoPrompt)
106106
_ = os.Setenv("AZURE_ENV_NAME", oldEnvName)
107107
_ = os.Setenv("AZD_TRACE_LOG_FILE", oldTraceFile)

cli/src/internal/executor/command_shell_test.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"path/filepath"
66
"runtime"
77
"testing"
8+
9+
"github.com/jongio/azd-core/shellutil"
810
)
911

1012
func TestBuildCommand_Bash(t *testing.T) {
@@ -201,7 +203,6 @@ func TestBuildCommand_CaseInsensitiveShellNames(t *testing.T) {
201203
}
202204

203205
func TestReadShebang_ValidShebang(t *testing.T) {
204-
exec := New(Config{})
205206
tmpDir := t.TempDir()
206207

207208
tests := []struct {
@@ -243,7 +244,7 @@ func TestReadShebang_ValidShebang(t *testing.T) {
243244
t.Fatal(err)
244245
}
245246

246-
result := exec.readShebang(scriptPath)
247+
result := shellutil.ReadShebang(scriptPath)
247248
if result != tt.expected {
248249
t.Errorf("Expected %q, got %q", tt.expected, result)
249250
}
@@ -252,7 +253,6 @@ func TestReadShebang_ValidShebang(t *testing.T) {
252253
}
253254

254255
func TestReadShebang_NoShebang(t *testing.T) {
255-
exec := New(Config{})
256256
tmpDir := t.TempDir()
257257

258258
tests := []struct {
@@ -280,7 +280,7 @@ func TestReadShebang_NoShebang(t *testing.T) {
280280
t.Fatal(err)
281281
}
282282

283-
result := exec.readShebang(scriptPath)
283+
result := shellutil.ReadShebang(scriptPath)
284284
if result != "" {
285285
t.Errorf("Expected empty string, got %q", result)
286286
}
@@ -289,10 +289,8 @@ func TestReadShebang_NoShebang(t *testing.T) {
289289
}
290290

291291
func TestReadShebang_FileErrors(t *testing.T) {
292-
exec := New(Config{})
293-
294292
t.Run("Nonexistent file", func(t *testing.T) {
295-
result := exec.readShebang("/nonexistent/file.sh")
293+
result := shellutil.ReadShebang("/nonexistent/file.sh")
296294
if result != "" {
297295
t.Errorf("Expected empty string for nonexistent file, got %q", result)
298296
}

0 commit comments

Comments
 (0)