v0.27.2 5960dd8

applier_test.go RawHistoryFolder

1package updater
2
3import (
4	"context"
5	"os"
6	"path/filepath"
7	"testing"
8
9	"github.com/bnema/dumber/internal/application/port"
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/require"
12)
13
14func TestApplier_SelfUpdateBlockedReason(t *testing.T) {
15	// Create a temporary directory for test state
16	tmpDir := t.TempDir()
17	applier := NewApplier(tmpDir)
18
19	ctx := context.Background()
20
21	// Get the reason - the actual result depends on the environment
22	reason := applier.SelfUpdateBlockedReason(ctx)
23
24	// Verify it returns a valid reason type
25	validReasons := []port.SelfUpdateBlockedReason{
26		port.SelfUpdateAllowed,
27		port.SelfUpdateBlockedFlatpak,
28		port.SelfUpdateBlockedPacman,
29		port.SelfUpdateBlockedNotWritable,
30	}
31
32	found := false
33	for _, valid := range validReasons {
34		if reason == valid {
35			found = true
36			break
37		}
38	}
39	assert.True(t, found, "SelfUpdateBlockedReason returned invalid reason: %q", reason)
40}
41
42func TestApplier_CanSelfUpdate_ConsistentWithReason(t *testing.T) {
43	tmpDir := t.TempDir()
44	applier := NewApplier(tmpDir)
45	ctx := context.Background()
46
47	canUpdate := applier.CanSelfUpdate(ctx)
48	reason := applier.SelfUpdateBlockedReason(ctx)
49
50	// If CanSelfUpdate returns true, reason should be SelfUpdateAllowed
51	if canUpdate {
52		assert.Equal(t, port.SelfUpdateAllowed, reason,
53			"CanSelfUpdate=true but SelfUpdateBlockedReason=%q", reason)
54	} else {
55		// If CanSelfUpdate returns false, reason should NOT be SelfUpdateAllowed
56		assert.NotEqual(t, port.SelfUpdateAllowed, reason,
57			"CanSelfUpdate=false but SelfUpdateBlockedReason is empty")
58	}
59}
60
61func TestApplier_StagingPaths(t *testing.T) {
62	tmpDir := t.TempDir()
63	applier := NewApplier(tmpDir)
64
65	// Test staging directory path
66	stagingDir := applier.stagingDir()
67	assert.Equal(t, filepath.Join(tmpDir, stagingDirName), stagingDir)
68
69	// Test staged binary path
70	stagedPath := applier.stagedBinaryPath()
71	assert.Equal(t, filepath.Join(tmpDir, stagingDirName, stagedBinaryName), stagedPath)
72}
73
74func TestApplier_HasStagedUpdate(t *testing.T) {
75	tmpDir := t.TempDir()
76	applier := NewApplier(tmpDir)
77	ctx := context.Background()
78
79	// Initially no staged update
80	assert.False(t, applier.HasStagedUpdate(ctx), "should have no staged update initially")
81
82	// Create staging directory and file
83	stagingDir := applier.stagingDir()
84	require.NoError(t, os.MkdirAll(stagingDir, 0o755))
85
86	stagedPath := applier.stagedBinaryPath()
87
88	// Create a non-executable file (should not count as staged)
89	require.NoError(t, os.WriteFile(stagedPath, []byte("test"), 0o644))
90	assert.False(t, applier.HasStagedUpdate(ctx), "non-executable file should not count as staged")
91
92	// Make it executable
93	require.NoError(t, os.Chmod(stagedPath, 0o755))
94	assert.True(t, applier.HasStagedUpdate(ctx), "executable file should count as staged")
95}
96
97func TestApplier_ClearStagedUpdate(t *testing.T) {
98	tmpDir := t.TempDir()
99	applier := NewApplier(tmpDir)
100	ctx := context.Background()
101
102	// Create staged update
103	stagingDir := applier.stagingDir()
104	require.NoError(t, os.MkdirAll(stagingDir, 0o755))
105	stagedPath := applier.stagedBinaryPath()
106	require.NoError(t, os.WriteFile(stagedPath, []byte("test"), 0o755))
107
108	assert.True(t, applier.HasStagedUpdate(ctx), "should have staged update")
109
110	// Clear it
111	require.NoError(t, applier.ClearStagedUpdate(ctx))
112
113	assert.False(t, applier.HasStagedUpdate(ctx), "should have no staged update after clear")
114
115	// Verify directory was removed
116	_, err := os.Stat(stagingDir)
117	assert.True(t, os.IsNotExist(err), "staging directory should be removed")
118}
119
120func TestApplier_StageUpdate(t *testing.T) {
121	tmpDir := t.TempDir()
122	applier := NewApplier(tmpDir)
123	ctx := context.Background()
124
125	// Create a fake binary to stage
126	srcBinary := filepath.Join(tmpDir, "new-binary")
127	binaryContent := []byte("#!/bin/sh\necho 'new version'\n")
128	require.NoError(t, os.WriteFile(srcBinary, binaryContent, 0o755))
129
130	// Stage the update
131	require.NoError(t, applier.StageUpdate(ctx, srcBinary))
132
133	// Verify it was staged
134	assert.True(t, applier.HasStagedUpdate(ctx), "should have staged update")
135
136	// Verify content was copied
137	stagedContent, err := os.ReadFile(applier.stagedBinaryPath())
138	require.NoError(t, err)
139	assert.Equal(t, binaryContent, stagedContent, "staged content should match source")
140
141	// Verify permissions
142	info, err := os.Stat(applier.stagedBinaryPath())
143	require.NoError(t, err)
144	assert.Equal(t, os.FileMode(0o755), info.Mode().Perm(), "staged binary should be executable")
145}
146
147func TestNewApplierFromXDG(t *testing.T) {
148	// This test verifies NewApplierFromXDG doesn't panic and returns valid applier
149	applier, err := NewApplierFromXDG()
150	require.NoError(t, err)
151	require.NotNil(t, applier)
152
153	// Verify it has a valid state directory
154	assert.NotEmpty(t, applier.stateDir)
155}
156
157func TestSelfUpdateBlockedReason_Constants(t *testing.T) {
158	// Verify the constants are distinct
159	reasons := []port.SelfUpdateBlockedReason{
160		port.SelfUpdateAllowed,
161		port.SelfUpdateBlockedFlatpak,
162		port.SelfUpdateBlockedPacman,
163		port.SelfUpdateBlockedNotWritable,
164	}
165
166	seen := make(map[port.SelfUpdateBlockedReason]bool)
167	for _, r := range reasons {
168		assert.False(t, seen[r], "duplicate reason: %q", r)
169		seen[r] = true
170	}
171
172	// Verify SelfUpdateAllowed is empty string
173	assert.Equal(t, port.SelfUpdateAllowed, port.SelfUpdateBlockedReason(""),
174		"SelfUpdateAllowed should be empty string")
175}

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 8

Tags 42

↑↓ moveenter openesc close