Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/modules/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func latestVersion(ctx context.Context, modPath string, repo vcs.Repo, comparato
return "", err
}
if len(tags) == 0 {
return "", fmt.Errorf("failed to retrieve the latest version: no tags found")
return repo.Latest(ctx)
}
max := slices.MaxFunc(tags, func(a, b string) int {
return comparator(module.Version{modPath, a}, module.Version{modPath, b})
Expand Down
58 changes: 48 additions & 10 deletions internal/modules/load_coverage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ func (f fakeFile) Read(_ []byte) (int, error) { return 0, io.EOF }
func (f fakeFile) Close() error { return nil }

type mockLatestRepo struct {
tags []string
tagsErr error
tags []string
tagsErr error
latest string
latestErr error
}

var _ vcs.Repo = (*mockLatestRepo)(nil)

func (m *mockLatestRepo) Tags(context.Context) ([]string, error) { return m.tags, m.tagsErr }
func (m *mockLatestRepo) Latest(context.Context) (string, error) { return "", nil }
func (m *mockLatestRepo) Latest(context.Context) (string, error) { return m.latest, m.latestErr }
func (m *mockLatestRepo) At(ref, localDir string) fs.FS { return os.DirFS(localDir) }
func (m *mockLatestRepo) Sync(ctx context.Context, ref, path, localDir string) error {
return nil
Expand Down Expand Up @@ -90,17 +92,32 @@ func TestLatestVersion_SelectsMaxByComparator(t *testing.T) {
}
}

func TestLatestVersion_NoTags(t *testing.T) {
repo := &mockLatestRepo{tags: []string{}}
func TestLatestVersion_NoTagsUsesLatestRef(t *testing.T) {
repo := &mockLatestRepo{latest: "deadbeef"}

cmp := func(v1, v2 module.Version) int { return strings.Compare(v1.Version, v2.Version) }
cmp := func(v1, v2 module.Version) int {
t.Fatalf("comparator called for no-tag repository: %v vs %v", v1, v2)
return 0
}

_, err := latestVersion(context.Background(), "towner/leafmod", repo, cmp)
got, err := latestVersion(context.Background(), "towner/leafmod", repo, cmp)
if err != nil {
t.Fatalf("latestVersion failed: %v", err)
}
if got != "deadbeef" {
t.Fatalf("latestVersion = %q, want %q", got, "deadbeef")
}
}

func TestLatestVersion_LatestError(t *testing.T) {
repo := &mockLatestRepo{latestErr: errors.New("forced latest error")}

_, err := latestVersion(context.Background(), "towner/leafmod", repo, func(module.Version, module.Version) int { return 0 })
if err == nil {
t.Fatal("expected error for no tags")
t.Fatal("expected latest error")
}
if !strings.Contains(err.Error(), "no tags found") {
t.Fatalf("error = %v, want contains %q", err, "no tags found")
if !strings.Contains(err.Error(), "forced latest error") {
t.Fatalf("error = %v, want contains %q", err, "forced latest error")
}
}

Expand Down Expand Up @@ -265,6 +282,27 @@ func TestLoad_EmptyVersion_LatestVersionTagsError(t *testing.T) {
}
}

func TestLoad_EmptyVersion_NoTagsUsesHeadRef(t *testing.T) {
fakeGitDir := t.TempDir()
fakeGit := "#!/bin/sh\nif [ \"$3\" = \"HEAD\" ]; then\n printf 'deadbeef\\tHEAD\\n'\nfi\n"
if err := os.WriteFile(filepath.Join(fakeGitDir, "git"), []byte(fakeGit), 0o755); err != nil {
t.Fatalf("write fake git: %v", err)
}
t.Setenv("PATH", fakeGitDir+string(os.PathListSeparator)+os.Getenv("PATH"))

store := setupTestStore(t, "testdata/load")
modules, err := Load(context.Background(), module.Version{Path: "towner/standalone"}, Options{FormulaStore: store})
if err != nil {
t.Fatalf("Load failed: %v", err)
}
if len(modules) != 1 {
t.Fatalf("loaded modules = %d, want 1", len(modules))
}
if modules[0].Version != "deadbeef" {
t.Fatalf("main version = %q, want %q", modules[0].Version, "deadbeef")
}
}

func TestResolveDeps_OnRequireMkdirTempError(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "tmp-file")
if err := os.WriteFile(tmpFile, []byte("not-a-dir"), 0644); err != nil {
Expand Down
41 changes: 41 additions & 0 deletions internal/modules/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,3 +949,44 @@ func TestIntegration_LoadWithEmptyVersion(t *testing.T) {
}
t.Logf("resolved version: %s", modules[0].Version)
}

func TestIntegration_LoadTaglessRepositoryAtHead(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}

const formulaRef = "57f92f7d1ac9673cd4c2e81d6d9777bd9340f7c9"
ctx := context.Background()

sourceRepo, err := vcs.NewRepo("github.com/rxi/log.c")
if err != nil {
t.Fatalf("create source repo failed: %v", err)
}
tags, err := sourceRepo.Tags(ctx)
if err != nil {
t.Fatalf("list source tags failed: %v", err)
}
if len(tags) != 0 {
t.Fatalf("source repository has tags %v, want no tags", tags)
}
head, err := sourceRepo.Latest(ctx)
if err != nil {
t.Fatalf("resolve source HEAD failed: %v", err)
}

formulaRepo, err := vcs.NewRepo("github.com/MeteorsLiu/llarhub")
if err != nil {
t.Fatalf("create formula repo failed: %v", err)
}
store := repo.New(t.TempDir(), &pinnedRepo{Repo: formulaRepo, ref: formulaRef})
modules, err := Load(ctx, module.Version{Path: "rxi/log.c"}, Options{FormulaStore: store})
if err != nil {
t.Fatalf("Load tagless repository failed: %v", err)
}
if len(modules) != 1 {
t.Fatalf("loaded modules = %d, want 1", len(modules))
}
if modules[0].Version != head {
t.Fatalf("resolved version = %q, want HEAD %q", modules[0].Version, head)
}
}
Loading