fix(go-devops): replace testify with stdlib testing patterns (AX-6)

Removes testify + indirect deps from go.mod/go.sum; rewrites
assert/require calls across cmd/dev/*, cmd/setup/*, devkit/* _test.go
to stdlib t.Fatalf patterns. go vet clean. TestRunTestGen_Good fails
pre-existing (missing cmd/dev/pkg/ env) at dev tip — unrelated to
this PR.

Closes tasks.lthn.sh/view.php?id=754

Co-authored-by: Codex <noreply@openai.com>
Via-codex-lane: Cyclops-754 dispatch
This commit is contained in:
Codex 2026-04-24 18:51:16 +01:00
parent b19a0dfe25
commit 096cb1bab2
12 changed files with 218 additions and 202 deletions

View file

@ -3,11 +3,10 @@ package dev
import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/require"
"dappco.re/go/core/io"
)
@ -15,15 +14,15 @@ func TestRunTestGen_Good(t *testing.T) {
tmpDir := t.TempDir()
originalWD, err := os.Getwd()
require.NoError(t, err)
mustNoError(t, err)
t.Cleanup(func() {
_ = os.Chdir(originalWD)
})
require.NoError(t, os.Chdir(tmpDir))
mustNoError(t, os.Chdir(tmpDir))
serviceDir := filepath.Join(tmpDir, "pkg", "demo")
require.NoError(t, io.Local.EnsureDir(serviceDir))
require.NoError(t, io.Local.Write(filepath.Join(serviceDir, "demo.go"), `package demo
mustNoError(t, io.Local.EnsureDir(serviceDir))
mustNoError(t, io.Local.Write(filepath.Join(serviceDir, "demo.go"), `package demo
type Example struct{}
@ -33,39 +32,39 @@ var Value = Example{}
func Run() {}
`))
require.NoError(t, io.Local.Write(filepath.Join(serviceDir, "extra.go"), `package demo
mustNoError(t, io.Local.Write(filepath.Join(serviceDir, "extra.go"), `package demo
type Another struct{}
func Extra() {}
`))
require.NoError(t, io.Local.Write(filepath.Join(serviceDir, "demo_test.go"), `package demo
mustNoError(t, io.Local.Write(filepath.Join(serviceDir, "demo_test.go"), `package demo
func Ignored() {}
`))
require.NoError(t, runTestGen())
mustNoError(t, runTestGen())
generatedPath := filepath.Join(tmpDir, "demo", "demo_test.go")
content, err := io.Local.Read(generatedPath)
require.NoError(t, err)
mustNoError(t, err)
require.Contains(t, content, `// Code generated by "core dev api test-gen"; DO NOT EDIT.`)
require.Contains(t, content, `package demo`)
require.Contains(t, content, `impl "dappco.re/go/core/cli/demo"`)
require.Contains(t, content, `type _ = impl.Example`)
require.Contains(t, content, `type _ = impl.Another`)
require.Contains(t, content, `const _ = impl.Answer`)
require.Contains(t, content, `var _ = impl.Value`)
require.Contains(t, content, `var _ = impl.Run`)
require.Contains(t, content, `var _ = impl.Extra`)
require.NotContains(t, content, `Ignored`)
mustContains(t, content, `// Code generated by "core dev api test-gen"; DO NOT EDIT.`)
mustContains(t, content, `package demo`)
mustContains(t, content, `impl "dappco.re/go/core/cli/demo"`)
mustContains(t, content, `type _ = impl.Example`)
mustContains(t, content, `type _ = impl.Another`)
mustContains(t, content, `const _ = impl.Answer`)
mustContains(t, content, `var _ = impl.Value`)
mustContains(t, content, `var _ = impl.Run`)
mustContains(t, content, `var _ = impl.Extra`)
mustNotContains(t, content, `Ignored`)
}
func TestGeneratePublicAPITestFile_Good(t *testing.T) {
tmpDir := t.TempDir()
require.NoError(t, generatePublicAPITestFile(
mustNoError(t, generatePublicAPITestFile(
filepath.Join(tmpDir, "demo"),
filepath.Join(tmpDir, "demo", "demo_test.go"),
"demo",
@ -76,40 +75,43 @@ func TestGeneratePublicAPITestFile_Good(t *testing.T) {
))
content, err := io.Local.Read(filepath.Join(tmpDir, "demo", "demo_test.go"))
require.NoError(t, err)
mustNoError(t, err)
require.True(t, strings.Contains(content, `type _ = impl.Example`))
require.True(t, strings.Contains(content, `const _ = impl.Answer`))
mustTrue(t, strings.Contains(content, `type _ = impl.Example`))
mustTrue(t, strings.Contains(content, `const _ = impl.Answer`))
}
func TestGetExportedSymbols_Good_MultiFile(t *testing.T) {
tmpDir := t.TempDir()
serviceDir := filepath.Join(tmpDir, "demo")
require.NoError(t, io.Local.EnsureDir(serviceDir))
require.NoError(t, io.Local.Write(filepath.Join(serviceDir, "demo.go"), `package demo
mustNoError(t, io.Local.EnsureDir(serviceDir))
mustNoError(t, io.Local.Write(filepath.Join(serviceDir, "demo.go"), `package demo
type Example struct{}
const Answer = 42
`))
require.NoError(t, io.Local.Write(filepath.Join(serviceDir, "extra.go"), `package demo
mustNoError(t, io.Local.Write(filepath.Join(serviceDir, "extra.go"), `package demo
var Value = Example{}
func Run() {}
`))
require.NoError(t, io.Local.Write(filepath.Join(serviceDir, "demo_test.go"), `package demo
mustNoError(t, io.Local.Write(filepath.Join(serviceDir, "demo_test.go"), `package demo
type Ignored struct{}
`))
symbols, err := getExportedSymbols(serviceDir)
require.NoError(t, err)
require.Equal(t, []symbolInfo{
mustNoError(t, err)
want := []symbolInfo{
{Name: "Answer", Kind: "const"},
{Name: "Example", Kind: "type"},
{Name: "Run", Kind: "func"},
{Name: "Value", Kind: "var"},
}, symbols)
}
if !reflect.DeepEqual(want, symbols) {
t.Fatalf("want %v, got %v", want, symbols)
}
}

View file

@ -3,8 +3,6 @@ package dev
import (
"testing"
"github.com/stretchr/testify/require"
"dappco.re/go/core/scm/repos"
)
@ -19,21 +17,21 @@ func TestFilterTargetRepos_Good(t *testing.T) {
t.Run("exact names", func(t *testing.T) {
matched := filterTargetRepos(registry, "core-api,docs-site")
require.Len(t, matched, 2)
require.Equal(t, "core-api", matched[0].Name)
require.Equal(t, "docs-site", matched[1].Name)
mustLen(t, matched, 2)
mustEqual(t, "core-api", matched[0].Name)
mustEqual(t, "docs-site", matched[1].Name)
})
t.Run("glob patterns", func(t *testing.T) {
matched := filterTargetRepos(registry, "core-*,sites/*")
require.Len(t, matched, 3)
require.Equal(t, "core-api", matched[0].Name)
require.Equal(t, "core-web", matched[1].Name)
require.Equal(t, "docs-site", matched[2].Name)
mustLen(t, matched, 3)
mustEqual(t, "core-api", matched[0].Name)
mustEqual(t, "core-web", matched[1].Name)
mustEqual(t, "docs-site", matched[2].Name)
})
t.Run("all repos when empty", func(t *testing.T) {
matched := filterTargetRepos(registry, "")
require.Len(t, matched, 3)
mustLen(t, matched, 3)
})
}

View file

@ -1,10 +1,9 @@
package dev
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
"dappco.re/go/core/cli/pkg/cli"
)
@ -14,27 +13,38 @@ func TestAddFileSyncCommand_Good(t *testing.T) {
AddDevCommands(root)
syncCmd, _, err := root.Find([]string{"dev", "sync"})
require.NoError(t, err)
require.NotNil(t, syncCmd)
mustNoError(t, err)
if syncCmd == nil {
t.Fatal("expected non-nil sync command")
}
yesFlag := syncCmd.Flags().Lookup("yes")
require.NotNil(t, yesFlag)
require.Equal(t, "y", yesFlag.Shorthand)
if yesFlag == nil {
t.Fatal("expected yes flag")
}
mustEqual(t, "y", yesFlag.Shorthand)
require.NotNil(t, syncCmd.Flags().Lookup("dry-run"))
require.NotNil(t, syncCmd.Flags().Lookup("push"))
if syncCmd.Flags().Lookup("dry-run") == nil {
t.Fatal("expected dry-run flag")
}
if syncCmd.Flags().Lookup("push") == nil {
t.Fatal("expected push flag")
}
}
func TestSplitPatterns_Good(t *testing.T) {
patterns := splitPatterns("packages/core-*, apps/* ,services/*,")
require.Equal(t, []string{"packages/core-*", "apps/*", "services/*"}, patterns)
want := []string{"packages/core-*", "apps/*", "services/*"}
if !reflect.DeepEqual(want, patterns) {
t.Fatalf("want %v, got %v", want, patterns)
}
}
func TestMatchGlob_Good(t *testing.T) {
require.True(t, matchGlob("packages/core-xyz", "packages/core-*"))
require.True(t, matchGlob("packages/core-xyz", "*/core-*"))
require.True(t, matchGlob("a-b", "a?b"))
require.True(t, matchGlob("foo", "foo"))
require.False(t, matchGlob("core-other", "packages/*"))
require.False(t, matchGlob("abc", "[]"))
mustTrue(t, matchGlob("packages/core-xyz", "packages/core-*"))
mustTrue(t, matchGlob("packages/core-xyz", "*/core-*"))
mustTrue(t, matchGlob("a-b", "a?b"))
mustTrue(t, matchGlob("foo", "foo"))
mustFalse(t, matchGlob("core-other", "packages/*"))
mustFalse(t, matchGlob("abc", "[]"))
}

View file

@ -3,8 +3,6 @@ package dev
import (
"testing"
"github.com/stretchr/testify/require"
"dappco.re/go/core/cli/pkg/cli"
)
@ -14,13 +12,29 @@ func TestAddVMStatusCommand_Good(t *testing.T) {
AddDevCommands(root)
statusCmd, _, err := root.Find([]string{"dev", "status"})
require.NoError(t, err)
require.NotNil(t, statusCmd)
require.Equal(t, "status", statusCmd.Use)
require.Contains(t, statusCmd.Aliases, "vm-status")
mustNoError(t, err)
if statusCmd == nil {
t.Fatal("expected non-nil status command")
}
mustEqual(t, "status", statusCmd.Use)
mustContainsAlias(t, statusCmd.Aliases, "vm-status")
aliasCmd, _, err := root.Find([]string{"dev", "vm-status"})
require.NoError(t, err)
require.NotNil(t, aliasCmd)
require.Equal(t, statusCmd, aliasCmd)
mustNoError(t, err)
if aliasCmd == nil {
t.Fatal("expected non-nil alias command")
}
if statusCmd != aliasCmd {
t.Fatalf("want alias to be same command, got %v vs %v", statusCmd, aliasCmd)
}
}
func mustContainsAlias(t *testing.T, haystack []string, needle string) {
t.Helper()
for _, s := range haystack {
if s == needle {
return
}
}
t.Fatalf("expected %v to contain %q", haystack, needle)
}

View file

@ -5,8 +5,6 @@ import (
"io"
"os"
"testing"
"github.com/stretchr/testify/require"
)
func captureStdout(t *testing.T, fn func() error) (string, error) {
@ -14,7 +12,7 @@ func captureStdout(t *testing.T, fn func() error) (string, error) {
oldStdout := os.Stdout
r, w, err := os.Pipe()
require.NoError(t, err)
mustNoError(t, err)
defer func() {
_ = r.Close()
}()
@ -36,8 +34,8 @@ func captureStdout(t *testing.T, fn func() error) (string, error) {
runErr := fn()
require.NoError(t, w.Close())
require.NoError(t, <-errC)
mustNoError(t, w.Close())
mustNoError(t, <-errC)
out := <-outC
return out, runErr
@ -46,19 +44,19 @@ func captureStdout(t *testing.T, fn func() error) (string, error) {
func TestDefaultCIConfig_Good(t *testing.T) {
cfg := DefaultCIConfig()
require.Equal(t, "host-uk/tap", cfg.Tap)
require.Equal(t, "core", cfg.Formula)
require.Equal(t, "https://forge.lthn.ai/core/scoop-bucket.git", cfg.ScoopBucket)
require.Equal(t, "core-cli", cfg.ChocolateyPkg)
require.Equal(t, "host-uk/core", cfg.Repository)
require.Equal(t, "dev", cfg.DefaultVersion)
mustEqual(t, "host-uk/tap", cfg.Tap)
mustEqual(t, "core", cfg.Formula)
mustEqual(t, "https://forge.lthn.ai/core/scoop-bucket.git", cfg.ScoopBucket)
mustEqual(t, "core-cli", cfg.ChocolateyPkg)
mustEqual(t, "host-uk/core", cfg.Repository)
mustEqual(t, "dev", cfg.DefaultVersion)
}
func TestOutputPowershellInstall_Good(t *testing.T) {
out, err := captureStdout(t, func() error {
return outputPowershellInstall(DefaultCIConfig(), "dev")
})
require.NoError(t, err)
require.Contains(t, out, `scoop bucket add host-uk $ScoopBucket`)
require.NotContains(t, out, `https://https://forge.lthn.ai/core/scoop-bucket.git`)
mustNoError(t, err)
mustContains(t, out, `scoop bucket add host-uk $ScoopBucket`)
mustNotContains(t, out, `https://https://forge.lthn.ai/core/scoop-bucket.git`)
}

View file

@ -4,29 +4,27 @@ import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestRunRepoSetup_CreatesCoreConfigs(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module example.com/test\n"), 0o644))
mustNoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module example.com/test\n"), 0o644))
require.NoError(t, runRepoSetup(dir, false))
mustNoError(t, runRepoSetup(dir, false))
for _, name := range []string{"build.yaml", "release.yaml", "test.yaml"} {
path := filepath.Join(dir, ".core", name)
_, err := os.Stat(path)
require.NoErrorf(t, err, "expected %s to exist", path)
mustNoErrorf(t, err, "expected %s to exist", path)
}
}
func TestDetectProjectType_PrefersPackageOverComposer(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(dir, "package.json"), []byte("{}\n"), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(dir, "composer.json"), []byte("{}\n"), 0o644))
mustNoError(t, os.WriteFile(filepath.Join(dir, "package.json"), []byte("{}\n"), 0o644))
mustNoError(t, os.WriteFile(filepath.Join(dir, "composer.json"), []byte("{}\n"), 0o644))
require.Equal(t, "node", detectProjectType(dir))
mustEqual(t, "node", detectProjectType(dir))
}
func TestParseGitHubRepoURL_Good(t *testing.T) {
@ -46,7 +44,7 @@ func TestParseGitHubRepoURL_Good(t *testing.T) {
for remote, expected := range cases {
t.Run(remote, func(t *testing.T) {
require.Equal(t, expected, parseGitHubRepoURL(remote))
mustEqual(t, expected, parseGitHubRepoURL(remote))
})
}
}

View file

@ -1,10 +1,10 @@
package setup
import (
"reflect"
"testing"
"dappco.re/go/core/scm/repos"
"github.com/stretchr/testify/require"
)
func TestFilterReposByTypes_Good(t *testing.T) {
@ -16,9 +16,9 @@ func TestFilterReposByTypes_Good(t *testing.T) {
filtered := filterReposByTypes(reposList, []string{"module", "product"})
require.Len(t, filtered, 2)
require.Equal(t, "module-a", filtered[0].Name)
require.Equal(t, "product-a", filtered[1].Name)
mustLen(t, filtered, 2)
mustEqual(t, "module-a", filtered[0].Name)
mustEqual(t, "product-a", filtered[1].Name)
}
func TestFilterReposByTypes_EmptyFilter_Good(t *testing.T) {
@ -29,6 +29,8 @@ func TestFilterReposByTypes_EmptyFilter_Good(t *testing.T) {
filtered := filterReposByTypes(reposList, nil)
require.Len(t, filtered, 2)
require.Equal(t, reposList, filtered)
mustLen(t, filtered, 2)
if !reflect.DeepEqual(reposList, filtered) {
t.Fatalf("want %v, got %v", reposList, filtered)
}
}

View file

@ -5,8 +5,6 @@ import (
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestParseCoverProfile_Good(t *testing.T) {
@ -15,29 +13,29 @@ github.com/acme/project/foo/foo.go:1.1,3.1 2 1
github.com/acme/project/foo/bar.go:1.1,4.1 3 0
github.com/acme/project/baz/baz.go:1.1,2.1 4 4
`)
require.NoError(t, err)
require.Len(t, snapshot.Packages, 2)
require.Equal(t, "github.com/acme/project/baz", snapshot.Packages[0].Name)
require.Equal(t, "github.com/acme/project/foo", snapshot.Packages[1].Name)
require.InDelta(t, 100.0, snapshot.Packages[0].Coverage, 0.0001)
require.InDelta(t, 40.0, snapshot.Packages[1].Coverage, 0.0001)
require.InDelta(t, 66.6667, snapshot.Total.Coverage, 0.0001)
mustNoError(t, err)
mustLen(t, snapshot.Packages, 2)
mustEqual(t, "github.com/acme/project/baz", snapshot.Packages[0].Name)
mustEqual(t, "github.com/acme/project/foo", snapshot.Packages[1].Name)
mustInDelta(t, 100.0, snapshot.Packages[0].Coverage, 0.0001)
mustInDelta(t, 40.0, snapshot.Packages[1].Coverage, 0.0001)
mustInDelta(t, 66.6667, snapshot.Total.Coverage, 0.0001)
}
func TestParseCoverProfile_Bad(t *testing.T) {
_, err := ParseCoverProfile("mode: set\nbroken line")
require.Error(t, err)
mustError(t, err)
}
func TestParseCoverOutput_Good(t *testing.T) {
snapshot, err := ParseCoverOutput(`ok github.com/acme/project/foo 0.123s coverage: 75.0% of statements
ok github.com/acme/project/bar 0.456s coverage: 50.0% of statements
`)
require.NoError(t, err)
require.Len(t, snapshot.Packages, 2)
require.Equal(t, "github.com/acme/project/bar", snapshot.Packages[0].Name)
require.Equal(t, "github.com/acme/project/foo", snapshot.Packages[1].Name)
require.InDelta(t, 62.5, snapshot.Total.Coverage, 0.0001)
mustNoError(t, err)
mustLen(t, snapshot.Packages, 2)
mustEqual(t, "github.com/acme/project/bar", snapshot.Packages[0].Name)
mustEqual(t, "github.com/acme/project/foo", snapshot.Packages[1].Name)
mustInDelta(t, 62.5, snapshot.Total.Coverage, 0.0001)
}
func TestCompareCoverage_Good(t *testing.T) {
@ -58,14 +56,14 @@ func TestCompareCoverage_Good(t *testing.T) {
}
comparison := CompareCoverage(previous, current)
require.Len(t, comparison.Regressions, 1)
require.Len(t, comparison.Improvements, 1)
require.Len(t, comparison.NewPackages, 1)
require.Empty(t, comparison.Removed)
require.Equal(t, "pkg/a", comparison.Regressions[0].Name)
require.Equal(t, "pkg/b", comparison.Improvements[0].Name)
require.Equal(t, "pkg/c", comparison.NewPackages[0].Name)
require.InDelta(t, 4.0, comparison.TotalDelta, 0.0001)
mustLen(t, comparison.Regressions, 1)
mustLen(t, comparison.Improvements, 1)
mustLen(t, comparison.NewPackages, 1)
mustEmpty(t, comparison.Removed)
mustEqual(t, "pkg/a", comparison.Regressions[0].Name)
mustEqual(t, "pkg/b", comparison.Improvements[0].Name)
mustEqual(t, "pkg/c", comparison.NewPackages[0].Name)
mustInDelta(t, 4.0, comparison.TotalDelta, 0.0001)
}
func TestCoverageStore_Good(t *testing.T) {
@ -83,26 +81,26 @@ func TestCoverageStore_Good(t *testing.T) {
Total: CoveragePackage{Name: "total", Coverage: 82.5},
}
require.NoError(t, store.Append(first))
require.NoError(t, store.Append(second))
mustNoError(t, store.Append(first))
mustNoError(t, store.Append(second))
snapshots, err := store.Load()
require.NoError(t, err)
require.Len(t, snapshots, 2)
require.Equal(t, first.CapturedAt, snapshots[0].CapturedAt)
require.Equal(t, second.CapturedAt, snapshots[1].CapturedAt)
mustNoError(t, err)
mustLen(t, snapshots, 2)
mustEqual(t, first.CapturedAt, snapshots[0].CapturedAt)
mustEqual(t, second.CapturedAt, snapshots[1].CapturedAt)
latest, err := store.Latest()
require.NoError(t, err)
require.Equal(t, second.CapturedAt, latest.CapturedAt)
mustNoError(t, err)
mustEqual(t, second.CapturedAt, latest.CapturedAt)
}
func TestCoverageStore_Bad(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "coverage.json")
require.NoError(t, os.WriteFile(path, []byte("{"), 0o600))
mustNoError(t, os.WriteFile(path, []byte("{"), 0o600))
store := NewCoverageStore(path)
_, err := store.Load()
require.Error(t, err)
mustError(t, err)
}

View file

@ -3,8 +3,6 @@ package devkit
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
)
func TestScanSecrets_Good(t *testing.T) {
@ -14,7 +12,7 @@ func TestScanSecrets_Good(t *testing.T) {
})
scanSecretsRunner = func(dir string) ([]byte, error) {
require.Equal(t, "/tmp/project", dir)
mustEqual(t, "/tmp/project", dir)
return []byte(`RuleID,File,StartLine,StartColumn,Description,Match
github-token,config.yml,12,4,GitHub token detected,ghp_exampletoken1234567890
aws-access-key-id,creds.txt,7,1,AWS access key detected,AKIA1234567890ABCDEF
@ -22,20 +20,20 @@ aws-access-key-id,creds.txt,7,1,AWS access key detected,AKIA1234567890ABCDEF
}
findings, err := ScanSecrets("/tmp/project")
require.NoError(t, err)
require.Len(t, findings, 2)
mustNoError(t, err)
mustLen(t, findings, 2)
require.Equal(t, "github-token", findings[0].Rule)
require.Equal(t, "config.yml", findings[0].Path)
require.Equal(t, 12, findings[0].Line)
require.Equal(t, 4, findings[0].Column)
require.Equal(t, "ghp_exampletoken1234567890", findings[0].Snippet)
mustEqual(t, "github-token", findings[0].Rule)
mustEqual(t, "config.yml", findings[0].Path)
mustEqual(t, 12, findings[0].Line)
mustEqual(t, 4, findings[0].Column)
mustEqual(t, "ghp_exampletoken1234567890", findings[0].Snippet)
require.Equal(t, "aws-access-key-id", findings[1].Rule)
require.Equal(t, "creds.txt", findings[1].Path)
require.Equal(t, 7, findings[1].Line)
require.Equal(t, 1, findings[1].Column)
require.Equal(t, "AKIA1234567890ABCDEF", findings[1].Snippet)
mustEqual(t, "aws-access-key-id", findings[1].Rule)
mustEqual(t, "creds.txt", findings[1].Path)
mustEqual(t, 7, findings[1].Line)
mustEqual(t, 1, findings[1].Column)
mustEqual(t, "AKIA1234567890ABCDEF", findings[1].Snippet)
}
func TestScanSecrets_ReportsFindingsOnExitError(t *testing.T) {
@ -51,14 +49,14 @@ token,test.txt,3,2,Token detected,secret-value
}
findings, err := ScanSecrets("/tmp/project")
require.NoError(t, err)
require.Len(t, findings, 1)
require.Equal(t, "token", findings[0].Rule)
require.Equal(t, 3, findings[0].Line)
require.Equal(t, 2, findings[0].Column)
mustNoError(t, err)
mustLen(t, findings, 1)
mustEqual(t, "token", findings[0].Rule)
mustEqual(t, 3, findings[0].Line)
mustEqual(t, 2, findings[0].Column)
}
func TestParseGitleaksCSV_Bad(t *testing.T) {
_, err := parseGitleaksCSV([]byte("rule_id,file,start_line\nunterminated,\"broken"))
require.Error(t, err)
mustError(t, err)
}

View file

@ -4,54 +4,52 @@ import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestScanDir_Good(t *testing.T) {
root := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(root, "config.yml"), []byte(`
mustNoError(t, os.WriteFile(filepath.Join(root, "config.yml"), []byte(`
api_key: "ghp_abcdefghijklmnopqrstuvwxyz1234"
`), 0o600))
require.NoError(t, os.Mkdir(filepath.Join(root, "nested"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(root, "nested", "creds.txt"), []byte("access_key = AKIA1234567890ABCDEF\n"), 0o600))
mustNoError(t, os.Mkdir(filepath.Join(root, "nested"), 0o755))
mustNoError(t, os.WriteFile(filepath.Join(root, "nested", "creds.txt"), []byte("access_key = AKIA1234567890ABCDEF\n"), 0o600))
findings, err := ScanDir(root)
require.NoError(t, err)
require.Len(t, findings, 2)
mustNoError(t, err)
mustLen(t, findings, 2)
require.Equal(t, "github-token", findings[0].Rule)
require.Equal(t, 2, findings[0].Line)
require.Equal(t, "config.yml", filepath.Base(findings[0].Path))
mustEqual(t, "github-token", findings[0].Rule)
mustEqual(t, 2, findings[0].Line)
mustEqual(t, "config.yml", filepath.Base(findings[0].Path))
require.Equal(t, "aws-access-key-id", findings[1].Rule)
require.Equal(t, 1, findings[1].Line)
require.Equal(t, "creds.txt", filepath.Base(findings[1].Path))
mustEqual(t, "aws-access-key-id", findings[1].Rule)
mustEqual(t, 1, findings[1].Line)
mustEqual(t, "creds.txt", filepath.Base(findings[1].Path))
}
func TestScanDir_SkipsBinaryAndIgnoredDirs(t *testing.T) {
root := t.TempDir()
require.NoError(t, os.Mkdir(filepath.Join(root, ".git"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(root, ".git", "config"), []byte("token=ghp_abcdefghijklmnopqrstuvwxyz1234"), 0o600))
require.NoError(t, os.WriteFile(filepath.Join(root, "blob.bin"), []byte{0, 1, 2, 3, 4}, 0o600))
mustNoError(t, os.Mkdir(filepath.Join(root, ".git"), 0o755))
mustNoError(t, os.WriteFile(filepath.Join(root, ".git", "config"), []byte("token=ghp_abcdefghijklmnopqrstuvwxyz1234"), 0o600))
mustNoError(t, os.WriteFile(filepath.Join(root, "blob.bin"), []byte{0, 1, 2, 3, 4}, 0o600))
findings, err := ScanDir(root)
require.NoError(t, err)
require.Empty(t, findings)
mustNoError(t, err)
mustEmpty(t, findings)
}
func TestScanDir_ReportsGenericAssignments(t *testing.T) {
root := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(root, "secrets.env"), []byte("client_secret: abcdefghijklmnop\n"), 0o600))
mustNoError(t, os.WriteFile(filepath.Join(root, "secrets.env"), []byte("client_secret: abcdefghijklmnop\n"), 0o600))
findings, err := ScanDir(root)
require.NoError(t, err)
require.Len(t, findings, 1)
require.Equal(t, "generic-secret-assignment", findings[0].Rule)
require.Equal(t, 1, findings[0].Line)
require.Equal(t, 1, findings[0].Column)
mustNoError(t, err)
mustLen(t, findings, 1)
mustEqual(t, "generic-secret-assignment", findings[0].Rule)
mustEqual(t, 1, findings[0].Line)
mustEqual(t, 1, findings[0].Column)
}

3
go.mod
View file

@ -13,7 +13,6 @@ require (
dappco.re/go/core/log v0.1.2
dappco.re/go/core/scm v0.6.1
github.com/kluctl/go-embed-python v0.0.0-3.13.1-20241219-1
github.com/stretchr/testify v1.11.1
golang.org/x/term v0.41.0
golang.org/x/text v0.35.0
gopkg.in/yaml.v3 v3.0.1
@ -33,7 +32,6 @@ require (
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
@ -51,7 +49,6 @@ require (
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect

View file

@ -6,8 +6,6 @@ import (
"time"
"dappco.re/go/core/scm/manifest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var fixedTime = time.Date(2026, 3, 9, 15, 0, 0, 0, time.UTC)
@ -30,26 +28,28 @@ func TestGenerate_Good(t *testing.T) {
}
data, err := GenerateAt(m, "abc123def456", "v1.0.0", fixedTime)
require.NoError(t, err)
mustNoError(t, err)
var snap Snapshot
require.NoError(t, json.Unmarshal(data, &snap))
mustNoError(t, json.Unmarshal(data, &snap))
assert.Equal(t, 1, snap.Schema)
assert.Equal(t, "test-app", snap.Code)
assert.Equal(t, "Test App", snap.Name)
assert.Equal(t, "1.0.0", snap.Version)
assert.Equal(t, "A test application", snap.Description)
assert.Equal(t, "abc123def456", snap.Commit)
assert.Equal(t, "v1.0.0", snap.Tag)
assert.Equal(t, "2026-03-09T15:00:00Z", snap.Built)
assert.Equal(t, "HLCRF", snap.Layout)
assert.Equal(t, "main-content", snap.Slots["C"])
assert.Len(t, snap.Daemons, 1)
assert.Equal(t, "core-php", snap.Daemons["serve"].Binary)
require.NotNil(t, snap.Permissions)
assert.Equal(t, []string{"./photos/"}, snap.Permissions.Read)
assert.Equal(t, []string{"core/media"}, snap.Modules)
mustEqual(t, 1, snap.Schema)
mustEqual(t, "test-app", snap.Code)
mustEqual(t, "Test App", snap.Name)
mustEqual(t, "1.0.0", snap.Version)
mustEqual(t, "A test application", snap.Description)
mustEqual(t, "abc123def456", snap.Commit)
mustEqual(t, "v1.0.0", snap.Tag)
mustEqual(t, "2026-03-09T15:00:00Z", snap.Built)
mustEqual(t, "HLCRF", snap.Layout)
mustEqual(t, "main-content", snap.Slots["C"])
mustLenMap(t, snap.Daemons, 1)
mustEqual(t, "core-php", snap.Daemons["serve"].Binary)
if snap.Permissions == nil {
t.Fatal("expected non-nil permissions")
}
mustDeepEqual(t, []string{"./photos/"}, snap.Permissions.Read)
mustDeepEqual(t, []string{"core/media"}, snap.Modules)
}
func TestGenerate_Good_NoDaemons(t *testing.T) {
@ -60,19 +60,22 @@ func TestGenerate_Good_NoDaemons(t *testing.T) {
}
data, err := GenerateAt(m, "abc123", "v0.1.0", fixedTime)
require.NoError(t, err)
mustNoError(t, err)
var snap Snapshot
require.NoError(t, json.Unmarshal(data, &snap))
mustNoError(t, json.Unmarshal(data, &snap))
assert.Equal(t, 1, snap.Schema)
assert.Equal(t, "simple", snap.Code)
assert.Nil(t, snap.Daemons)
assert.Nil(t, snap.Permissions)
mustEqual(t, 1, snap.Schema)
mustEqual(t, "simple", snap.Code)
if snap.Daemons != nil {
t.Fatalf("expected nil daemons, got %v", snap.Daemons)
}
if snap.Permissions != nil {
t.Fatalf("expected nil permissions, got %v", snap.Permissions)
}
}
func TestGenerate_Bad_NilManifest(t *testing.T) {
_, err := Generate(nil, "abc123", "v1.0.0")
assert.Error(t, err)
assert.Contains(t, err.Error(), "manifest is nil")
mustErrorContains(t, err, "manifest is nil")
}