Skip to content

Commit 2ba896f

Browse files
committed
chore: add test coverage for path mutations
1 parent dc072cb commit 2ba896f

File tree

1 file changed

+249
-0
lines changed

1 file changed

+249
-0
lines changed

pkg/build/paths_test.go

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// paths_test.go
2+
// Copyright 2022, 2023 Chainguard, Inc.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package build
17+
18+
import (
19+
"io/fs"
20+
"path/filepath"
21+
"testing"
22+
23+
"github.com/stretchr/testify/require"
24+
25+
apkfs "chainguard.dev/apko/pkg/apk/fs"
26+
"chainguard.dev/apko/pkg/build/types"
27+
"chainguard.dev/apko/pkg/options"
28+
)
29+
30+
func TestMutatePermissionsDirect(t *testing.T) {
31+
mfs := apkfs.NewMemFS()
32+
33+
// Create a file in the memfs.
34+
filePath := "testfile.txt"
35+
f, err := mfs.Create(filePath)
36+
require.NoError(t, err)
37+
f.Close()
38+
39+
// Change permissions and ownership.
40+
err = mutatePermissionsDirect(mfs, filePath, 0644, 1000, 1000)
41+
require.NoError(t, err)
42+
43+
// Check file mode.
44+
info, err := mfs.Stat(filePath)
45+
require.NoError(t, err)
46+
require.Equal(t, fs.FileMode(0644), info.Mode().Perm())
47+
48+
// Note: The in-memory FS may not support ownership checks.
49+
}
50+
51+
// TestMutatePermissionsRecursion verifies that mutatePermissionsDirect recurses
52+
// into directories and updates the permissions of contained files.
53+
func TestMutatePermissionsRecursion(t *testing.T) {
54+
mfs := apkfs.NewMemFS()
55+
56+
// Create a directory with a file inside.
57+
dirPath := "dirRec"
58+
err := mfs.MkdirAll(dirPath, 0700)
59+
require.NoError(t, err)
60+
filePath := filepath.Join(dirPath, "child.txt")
61+
f, err := mfs.Create(filePath)
62+
require.NoError(t, err)
63+
_, err = f.Write([]byte("content"))
64+
require.NoError(t, err)
65+
f.Close()
66+
67+
// Recursively change permissions of the directory and its children.
68+
err = mutatePermissionsDirect(mfs, dirPath, 0755, 2000, 2000)
69+
require.NoError(t, err)
70+
71+
// Check the directory's permissions.
72+
dirInfo, err := mfs.Stat(dirPath)
73+
require.NoError(t, err)
74+
require.Equal(t, fs.FileMode(0755), dirInfo.Mode().Perm())
75+
76+
// Check the child's permissions.
77+
childInfo, err := mfs.Stat(filePath)
78+
require.NoError(t, err)
79+
require.Equal(t, fs.FileMode(0755), childInfo.Mode().Perm())
80+
}
81+
82+
func TestMutateDirectory(t *testing.T) {
83+
mfs := apkfs.NewMemFS()
84+
opts := &options.Options{}
85+
86+
// Mutation to create a directory.
87+
mut := types.PathMutation{
88+
Type: "directory",
89+
Path: "testdir",
90+
Permissions: 0755,
91+
UID: 1000,
92+
GID: 1000,
93+
Recursive: false,
94+
}
95+
err := mutateDirectory(mfs, opts, mut)
96+
require.NoError(t, err)
97+
98+
// Check that the directory exists with the correct permissions.
99+
info, err := mfs.Stat("testdir")
100+
require.NoError(t, err)
101+
require.True(t, info.IsDir())
102+
require.Equal(t, fs.FileMode(0755), info.Mode().Perm())
103+
}
104+
105+
func TestMutateEmptyFile(t *testing.T) {
106+
mfs := apkfs.NewMemFS()
107+
opts := &options.Options{}
108+
target := filepath.Join("nonexistent", "subdir", "emptyfile.txt")
109+
110+
mut := types.PathMutation{
111+
Type: "empty-file",
112+
Path: target,
113+
}
114+
err := mutateEmptyFile(mfs, opts, mut)
115+
require.NoError(t, err)
116+
117+
// Verify that the file exists.
118+
info, err := mfs.Stat(target)
119+
require.NoError(t, err)
120+
require.False(t, info.IsDir())
121+
}
122+
123+
func TestMutateHardLink(t *testing.T) {
124+
mfs := apkfs.NewMemFS()
125+
opts := &options.Options{}
126+
127+
// Create a source file.
128+
src := "source.txt"
129+
f, err := mfs.Create(src)
130+
require.NoError(t, err)
131+
_, err = f.Write([]byte("hello"))
132+
require.NoError(t, err)
133+
f.Close()
134+
135+
target := "hardlink.txt"
136+
mut := types.PathMutation{
137+
Type: "hardlink",
138+
Path: target,
139+
Source: src,
140+
Permissions: 0644,
141+
UID: 1000,
142+
GID: 1000,
143+
}
144+
err = mutateHardLink(mfs, opts, mut)
145+
require.NoError(t, err)
146+
147+
// Verify that the target file exists and has the same content.
148+
data, err := mfs.ReadFile(target)
149+
require.NoError(t, err)
150+
require.Equal(t, []byte("hello"), data)
151+
}
152+
153+
func TestMutateSymLink(t *testing.T) {
154+
mfs := apkfs.NewMemFS()
155+
opts := &options.Options{}
156+
157+
// Create a target directory to link to.
158+
src := "real_target"
159+
err := mfs.MkdirAll(src, 0755)
160+
require.NoError(t, err)
161+
162+
target := "symlink.txt"
163+
mut := types.PathMutation{
164+
Type: "symlink",
165+
Path: target,
166+
Source: src,
167+
Permissions: 0777, // permissions here will be applied to the target of the symlink
168+
UID: 1000,
169+
GID: 1000,
170+
}
171+
err = mutateSymLink(mfs, opts, mut)
172+
require.NoError(t, err)
173+
174+
// Verify that the symlink still points to the correct source.
175+
linkTarget, err := mfs.Readlink(target)
176+
require.NoError(t, err)
177+
require.Equal(t, src, linkTarget)
178+
}
179+
180+
func TestMutatePaths(t *testing.T) {
181+
mfs := apkfs.NewMemFS()
182+
opts := &options.Options{}
183+
184+
ic := &types.ImageConfiguration{
185+
Paths: []types.PathMutation{
186+
{
187+
Type: "directory",
188+
Path: "dir1",
189+
Permissions: 0755,
190+
UID: 1000,
191+
GID: 1000,
192+
},
193+
{
194+
Type: "empty-file",
195+
Path: "dir1/empty.txt",
196+
},
197+
{
198+
Type: "hardlink",
199+
Path: "hardlink.txt",
200+
Source: "dir1/empty.txt",
201+
Permissions: 0644,
202+
UID: 1000,
203+
GID: 1000,
204+
},
205+
{
206+
Type: "symlink",
207+
Path: "symlink.txt",
208+
Source: "dir1",
209+
Permissions: 0777,
210+
UID: 1000,
211+
GID: 1000,
212+
},
213+
{
214+
Type: "permissions",
215+
Path: "dir1/empty.txt",
216+
Permissions: 0600,
217+
UID: 1001,
218+
GID: 1001,
219+
},
220+
},
221+
}
222+
err := mutatePaths(mfs, opts, ic)
223+
require.NoError(t, err)
224+
225+
// Verify that the directory was created.
226+
dirInfo, err := mfs.Stat("dir1")
227+
require.NoError(t, err)
228+
require.True(t, dirInfo.IsDir())
229+
// Expected permission is now 0777 because the symlink mutation follows the link
230+
// and resets the directory's permission.
231+
require.Equal(t, fs.FileMode(0777), dirInfo.Mode().Perm())
232+
233+
// Verify that the empty file exists and has the correct final permissions.
234+
fileInfo, err := mfs.Stat("dir1/empty.txt")
235+
require.NoError(t, err)
236+
require.False(t, fileInfo.IsDir())
237+
require.Equal(t, fs.FileMode(0600), fileInfo.Mode().Perm())
238+
239+
// Verify that the hardlink exists.
240+
data, err := mfs.ReadFile("hardlink.txt")
241+
require.NoError(t, err)
242+
// Since the empty file is empty, the data should be empty.
243+
require.Empty(t, data)
244+
245+
// Verify that the symlink still points to the correct target.
246+
linkTarget, err := mfs.Readlink("symlink.txt")
247+
require.NoError(t, err)
248+
require.Equal(t, "dir1", linkTarget)
249+
}

0 commit comments

Comments
 (0)