main 1fb2421

releases_index_test.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2package kvstore
3
4import (
5	"context"
6	"testing"
7	"time"
8
9	kv "bnema.dev/ermite-kv"
10	"bnema.dev/ermite/internal/domain"
11	"bnema.dev/ermite/internal/ports"
12	"github.com/stretchr/testify/require"
13)
14
15func TestReleaseLatestIndexBackfill(t *testing.T) {
16	ctx := t.Context()
17	dir := t.TempDir()
18	s, err := Open(ctx, dir)
19	require.NoError(t, err)
20	var repos []domain.RepoName
21	for _, raw := range []string{"backfill", "empty", "newest"} {
22		repo, err := domain.ParseRepoName(raw)
23		require.NoError(t, err)
24		repos = append(repos, repo)
25	}
26	for _, repo := range repos {
27		require.NoError(t, s.Create(ctx, domain.Repo{Name: repo, Visibility: domain.VisibilityPublic}))
28	}
29	var b kv.Batch
30	for i, repo := range repos {
31		for j := 1; j <= 5; j++ {
32			rel := domain.Release{ID: int64(i*10 + j), Repo: repo, Tag: string(rune('a' + j)), CreatedAt: time.Unix(int64(j), 0)}
33			rel.Draft = j == 5 || i == 1
34			rel.Prerelease = j == 4
35			rel.MakeLatest = j == 1 && i == 0
36			b.Put(relKey(repo, rel.ID), encodeRelease(rel))
37		}
38		// A stale key must also be removed for a repo with no eligible release.
39		b.Put(relLatestKey(repo), []byte("999"))
40	}
41	b.Delete([]byte(releaseLatestIndexMarker))
42	_, err = s.db.Apply(ctx, &b)
43	require.NoError(t, err)
44	require.NoError(t, s.Close(context.Background()))
45	s, err = Open(ctx, dir)
46	require.NoError(t, err)
47	defer func() { require.NoError(t, s.Close(context.Background())) }()
48	for i, want := range []int64{1, 0, 23} {
49		got, err := s.Releases().GetLatestRelease(ctx, repos[i])
50		if want == 0 {
51			require.ErrorIs(t, err, domain.ErrNotFound)
52		} else {
53			require.NoError(t, err)
54			require.Equal(t, want, got.ID)
55		}
56	}
57	require.NoError(t, s.buildReleaseLatestIndex(ctx))
58	got, err := s.Releases().GetLatestRelease(ctx, repos[0])
59	require.NoError(t, err)
60	require.EqualValues(t, 1, got.ID)
61	require.NoError(t, s.Delete(ctx, repos[0]))
62	_, err = s.Releases().GetLatestRelease(ctx, repos[0])
63	require.ErrorIs(t, err, domain.ErrNotFound)
64}
65
66func TestGetLatestReleaseAtomicUpdate(t *testing.T) {
67	ctx := t.Context()
68	s, err := Open(ctx, t.TempDir())
69	require.NoError(t, err)
70	defer func() { require.NoError(t, s.Close(context.Background())) }()
71	repo, err := domain.ParseRepoName("atomic")
72	require.NoError(t, err)
73	require.NoError(t, s.Create(ctx, domain.Repo{Name: repo, Visibility: domain.VisibilityPublic}))
74	a := domain.Release{ID: 1, Repo: repo, Tag: "a"}
75	b := domain.Release{ID: 2, Repo: repo, Tag: "b"}
76	store := s.Releases()
77	require.NoError(t, store.ApplyReleases(ctx, ports.ReleaseWrite{Repo: repo, PutReleases: []domain.Release{a, b}, SetLatest: true, LatestID: a.ID}))
78	a, err = store.GetLatestRelease(ctx, repo)
79	require.NoError(t, err)
80	require.EqualValues(t, 1, a.ID)
81	_, version, err := store.ReleaseUsage(ctx, repo)
82	require.NoError(t, err)
83	a.Prerelease = true
84	require.NoError(t, store.ApplyReleases(ctx, ports.ReleaseWrite{Repo: repo, UsageVersion: version, PutReleases: []domain.Release{a}, SetLatest: true, LatestID: b.ID}))
85	got, err := store.GetLatestRelease(ctx, repo)
86	require.NoError(t, err)
87	require.EqualValues(t, 2, got.ID)
88	// An asset-only/usage write leaves the index untouched, including its version.
89	before, ok := s.db.Get(ctx, relLatestKey(repo))
90	require.True(t, ok)
91	_, version, err = store.ReleaseUsage(ctx, repo)
92	require.NoError(t, err)
93	require.NoError(t, store.ApplyReleases(ctx, ports.ReleaseWrite{Repo: repo, UsageVersion: version, Used: 10}))
94	after, ok := s.db.Get(ctx, relLatestKey(repo))
95	require.True(t, ok)
96	require.Equal(t, before, after)
97}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close