Skip to content

Commit 9bfd4a4

Browse files
authored
test(codec): add tests for raw and tar codecs (#450)
* test(codec): add tests for raw and tar codecs Signed-off-by: Rishi Jat <rishijat098@gmail.com> * address copilot review Signed-off-by: Rishi Jat <rishijat098@gmail.com> --------- Signed-off-by: Rishi Jat <rishijat098@gmail.com>
1 parent c59e14d commit 9bfd4a4

1 file changed

Lines changed: 227 additions & 0 deletions

File tree

pkg/codec/codec_test.go

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
* Copyright 2025 The CNAI Authors
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+
17+
package codec
18+
19+
import (
20+
"bytes"
21+
"io"
22+
"os"
23+
"path/filepath"
24+
"strings"
25+
"testing"
26+
27+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
28+
"github.com/stretchr/testify/assert"
29+
"github.com/stretchr/testify/require"
30+
)
31+
32+
// --- Raw Codec Tests ---
33+
34+
func TestRawEncodeDecode(t *testing.T) {
35+
t.Parallel()
36+
dir := t.TempDir()
37+
content := []byte("hello world raw codec test")
38+
39+
// Write source file.
40+
srcPath := filepath.Join(dir, "input.bin")
41+
require.NoError(t, os.WriteFile(srcPath, content, 0644))
42+
43+
r := newRaw()
44+
45+
// Encode: should return a reader with the file's content.
46+
reader, err := r.Encode(srcPath, dir)
47+
require.NoError(t, err)
48+
if c, ok := reader.(io.Closer); ok {
49+
t.Cleanup(func() { _ = c.Close() })
50+
}
51+
52+
encoded, err := io.ReadAll(reader)
53+
require.NoError(t, err)
54+
assert.Equal(t, content, encoded)
55+
56+
// Decode: write the encoded bytes to an output directory.
57+
outputDir := filepath.Join(dir, "output")
58+
require.NoError(t, os.MkdirAll(outputDir, 0755))
59+
60+
desc := ocispec.Descriptor{Size: int64(len(content))}
61+
err = r.Decode(outputDir, "decoded.bin", bytes.NewReader(encoded), desc)
62+
require.NoError(t, err)
63+
64+
decoded, err := os.ReadFile(filepath.Join(outputDir, "decoded.bin"))
65+
require.NoError(t, err)
66+
assert.Equal(t, content, decoded)
67+
}
68+
69+
func TestRawEncodeEmpty(t *testing.T) {
70+
t.Parallel()
71+
dir := t.TempDir()
72+
content := []byte{}
73+
74+
srcPath := filepath.Join(dir, "empty.bin")
75+
require.NoError(t, os.WriteFile(srcPath, content, 0644))
76+
77+
r := newRaw()
78+
79+
reader, err := r.Encode(srcPath, dir)
80+
require.NoError(t, err)
81+
if c, ok := reader.(io.Closer); ok {
82+
t.Cleanup(func() { _ = c.Close() })
83+
}
84+
85+
encoded, err := io.ReadAll(reader)
86+
require.NoError(t, err)
87+
assert.Empty(t, encoded)
88+
}
89+
90+
func TestRawDecodeInvalidInput(t *testing.T) {
91+
t.Parallel()
92+
dir := t.TempDir()
93+
r := newRaw()
94+
95+
// Decode from a reader that always errors; the error should propagate.
96+
badReader := &errorReader{}
97+
desc := ocispec.Descriptor{Size: 10}
98+
99+
err := r.Decode(dir, "out.bin", badReader, desc)
100+
assert.Error(t, err)
101+
}
102+
103+
// errorReader is a reader that always returns an error.
104+
type errorReader struct{}
105+
106+
func (e *errorReader) Read([]byte) (int, error) {
107+
return 0, io.ErrUnexpectedEOF
108+
}
109+
110+
// --- Tar Codec Tests ---
111+
112+
func TestTarArchiveSingleFile(t *testing.T) {
113+
t.Parallel()
114+
srcDir := t.TempDir()
115+
content := []byte("single file content")
116+
117+
filePath := filepath.Join(srcDir, "file.txt")
118+
require.NoError(t, os.WriteFile(filePath, content, 0644))
119+
120+
c := newTar()
121+
122+
reader, err := c.Encode(filePath, srcDir)
123+
require.NoError(t, err)
124+
if c, ok := reader.(io.Closer); ok {
125+
t.Cleanup(func() { _ = c.Close() })
126+
}
127+
128+
// Read the tar stream fully so it can be used for extraction.
129+
tarData, err := io.ReadAll(reader)
130+
require.NoError(t, err)
131+
assert.NotEmpty(t, tarData)
132+
133+
// Extract and verify.
134+
extractDir := t.TempDir()
135+
desc := ocispec.Descriptor{}
136+
err = c.Decode(extractDir, "file.txt", bytes.NewReader(tarData), desc)
137+
require.NoError(t, err)
138+
139+
extracted, err := os.ReadFile(filepath.Join(extractDir, "file.txt"))
140+
require.NoError(t, err)
141+
assert.Equal(t, content, extracted)
142+
}
143+
144+
func TestTarArchiveMultipleFiles(t *testing.T) {
145+
t.Parallel()
146+
srcDir := t.TempDir()
147+
148+
files := map[string]string{
149+
"a.txt": "content of a",
150+
"b.txt": "content of b",
151+
"sub/c.txt": "content of c in sub",
152+
}
153+
154+
for name, data := range files {
155+
p := filepath.Join(srcDir, name)
156+
require.NoError(t, os.MkdirAll(filepath.Dir(p), 0755))
157+
require.NoError(t, os.WriteFile(p, []byte(data), 0644))
158+
}
159+
160+
c := newTar()
161+
162+
// Archive the entire directory.
163+
reader, err := c.Encode(srcDir, filepath.Dir(srcDir))
164+
require.NoError(t, err)
165+
if c, ok := reader.(io.Closer); ok {
166+
t.Cleanup(func() { _ = c.Close() })
167+
}
168+
169+
tarData, err := io.ReadAll(reader)
170+
require.NoError(t, err)
171+
assert.NotEmpty(t, tarData)
172+
173+
// Extract.
174+
extractDir := t.TempDir()
175+
desc := ocispec.Descriptor{}
176+
err = c.Decode(extractDir, "", bytes.NewReader(tarData), desc)
177+
require.NoError(t, err)
178+
179+
// The archive was created relative to filepath.Dir(srcDir), so the
180+
// extracted tree includes the base name of srcDir as a prefix.
181+
base := filepath.Base(srcDir)
182+
for name, expected := range files {
183+
got, err := os.ReadFile(filepath.Join(extractDir, base, name))
184+
require.NoError(t, err, "reading extracted file %s", name)
185+
assert.Equal(t, expected, string(got))
186+
}
187+
}
188+
189+
func TestTarExtractRoundtrip(t *testing.T) {
190+
t.Parallel()
191+
srcDir := t.TempDir()
192+
content := []byte("roundtrip data 1234567890")
193+
194+
require.NoError(t, os.WriteFile(filepath.Join(srcDir, "data.bin"), content, 0644))
195+
196+
c := newTar()
197+
198+
// Encode.
199+
reader, err := c.Encode(filepath.Join(srcDir, "data.bin"), srcDir)
200+
require.NoError(t, err)
201+
if c, ok := reader.(io.Closer); ok {
202+
t.Cleanup(func() { _ = c.Close() })
203+
}
204+
205+
tarData, err := io.ReadAll(reader)
206+
require.NoError(t, err)
207+
208+
// Decode.
209+
extractDir := t.TempDir()
210+
desc := ocispec.Descriptor{}
211+
require.NoError(t, c.Decode(extractDir, "data.bin", bytes.NewReader(tarData), desc))
212+
213+
got, err := os.ReadFile(filepath.Join(extractDir, "data.bin"))
214+
require.NoError(t, err)
215+
assert.Equal(t, content, got)
216+
}
217+
218+
func TestTarInvalidArchive(t *testing.T) {
219+
t.Parallel()
220+
c := newTar()
221+
extractDir := t.TempDir()
222+
desc := ocispec.Descriptor{}
223+
224+
// Feed garbage data as a tar stream.
225+
err := c.Decode(extractDir, "file.txt", strings.NewReader("this is not a tar"), desc)
226+
assert.Error(t, err)
227+
}

0 commit comments

Comments
 (0)