Skip to content

Commit d4853ab

Browse files
authored
Add some tests for dotc1z.File. (#598)
Also: - Return a grpc status error for empty output path. - Use context.Background() instead of t.Context() in c1file_test.go.
1 parent b00288b commit d4853ab

File tree

3 files changed

+191
-10
lines changed

3 files changed

+191
-10
lines changed

pkg/dotc1z/c1file_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func teardown() error {
7171
}
7272

7373
func TestC1Z(t *testing.T) {
74-
ctx := context.Background()
74+
ctx := t.Context()
7575
testFilePath := filepath.Join(c1zTests.workingDir, "test.c1z")
7676

7777
var opts []C1ZOption
@@ -159,7 +159,7 @@ func TestC1Z(t *testing.T) {
159159
}
160160

161161
func TestC1ZDecoder(t *testing.T) {
162-
ctx := context.Background()
162+
ctx := t.Context()
163163
testFilePath := filepath.Join(c1zTests.workingDir, "test-decoder.c1z")
164164

165165
// Open file
@@ -243,7 +243,7 @@ func TestC1ZDecoder(t *testing.T) {
243243
b.Reset()
244244

245245
// Test context cancel
246-
ctx, cancel := context.WithCancel(context.Background())
246+
ctx, cancel := context.WithCancel(t.Context())
247247
d, err = NewDecoder(c1zf, WithContext(ctx))
248248
require.NoError(t, err)
249249
_, err = io.Copy(b, d)
@@ -263,7 +263,7 @@ func TestC1ZDecoder(t *testing.T) {
263263
}
264264

265265
func TestC1ZInvalidFile(t *testing.T) {
266-
ctx := context.Background()
266+
ctx := t.Context()
267267
testFilePath := filepath.Join(c1zTests.workingDir, "test-invalid-file.c1z")
268268

269269
f, err := os.Create(testFilePath)
@@ -280,7 +280,7 @@ func TestC1ZInvalidFile(t *testing.T) {
280280
}
281281

282282
func TestC1ZStats(t *testing.T) {
283-
ctx := context.Background()
283+
ctx := t.Context()
284284
testFilePath := filepath.Join(c1zTests.workingDir, "test-stats.c1z")
285285

286286
f, err := NewC1ZFile(ctx, testFilePath, WithPragma("journal_mode", "WAL"))
@@ -331,7 +331,7 @@ func TestC1ZStats(t *testing.T) {
331331
}
332332

333333
func TestC1ZStatsPartialSync(t *testing.T) {
334-
ctx := context.Background()
334+
ctx := t.Context()
335335
testFilePath := filepath.Join(c1zTests.workingDir, "test-stats-partial-sync.c1z")
336336

337337
f, err := NewC1ZFile(ctx, testFilePath, WithPragma("journal_mode", "WAL"))
@@ -371,7 +371,7 @@ func TestC1ZStatsPartialSync(t *testing.T) {
371371
}
372372

373373
func TestC1ZStatsResourcesOnlySync(t *testing.T) {
374-
ctx := context.Background()
374+
ctx := t.Context()
375375
testFilePath := filepath.Join(c1zTests.workingDir, "test-stats-resources-only-sync.c1z")
376376

377377
f, err := NewC1ZFile(ctx, testFilePath, WithPragma("journal_mode", "WAL"))
@@ -426,7 +426,7 @@ func equalStats(t *testing.T, expectedStats map[string]int64, stats map[string]i
426426
}
427427

428428
func TestC1ZGrantStatsSync(t *testing.T) {
429-
ctx := context.Background()
429+
ctx := t.Context()
430430

431431
testFilePath := filepath.Join(c1zTests.workingDir, "test-grant-stats-sync.c1z")
432432

@@ -486,7 +486,7 @@ func TestC1ZGrantStatsSync(t *testing.T) {
486486
}
487487

488488
func TestC1ZReadOnlyMode(t *testing.T) {
489-
ctx := context.Background()
489+
ctx := t.Context()
490490
testFilePath := filepath.Join(c1zTests.workingDir, "test-readonly.c1z")
491491

492492
// First, create a c1z file with some data

pkg/dotc1z/file.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111

1212
"github.com/klauspost/compress/zstd"
1313
"go.uber.org/zap"
14+
"google.golang.org/grpc/codes"
15+
"google.golang.org/grpc/status"
1416
)
1517

1618
func loadC1z(filePath string, tmpDir string, opts ...DecoderOption) (string, error) {
@@ -59,7 +61,7 @@ func loadC1z(filePath string, tmpDir string, opts ...DecoderOption) (string, err
5961

6062
func saveC1z(dbFilePath string, outputFilePath string, encoderConcurrency int) error {
6163
if outputFilePath == "" {
62-
return errors.New("c1z: output file path not configured")
64+
return status.Errorf(codes.InvalidArgument, "c1z: output file path not configured")
6365
}
6466

6567
dbFile, err := os.Open(dbFilePath)

pkg/dotc1z/file_test.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package dotc1z
2+
3+
import (
4+
"io"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
"google.golang.org/grpc/codes"
11+
"google.golang.org/grpc/status"
12+
)
13+
14+
func TestLoadC1z(t *testing.T) {
15+
tmpDir := t.TempDir()
16+
17+
t.Run("temp directory cleanup on error", func(t *testing.T) {
18+
// Create a file that will cause an error during decoding
19+
invalidFile := filepath.Join(tmpDir, "invalid2.c1z")
20+
err := os.WriteFile(invalidFile, []byte("invalid"), 0600)
21+
require.NoError(t, err)
22+
defer os.Remove(invalidFile)
23+
24+
// Try to load it - should fail and clean up temp dir
25+
dbPath, err := loadC1z(invalidFile, tmpDir)
26+
require.Error(t, err)
27+
require.Empty(t, dbPath)
28+
})
29+
30+
t.Run("custom tmpDir", func(t *testing.T) {
31+
customTmpDir := filepath.Join(tmpDir, "custom")
32+
err := os.MkdirAll(customTmpDir, 0755)
33+
require.NoError(t, err)
34+
defer os.RemoveAll(customTmpDir)
35+
36+
nonExistentPath := filepath.Join(tmpDir, "nonexistent2.c1z")
37+
dbPath, err := loadC1z(nonExistentPath, customTmpDir)
38+
require.NoError(t, err)
39+
require.NotEmpty(t, dbPath)
40+
require.FileExists(t, dbPath)
41+
42+
// Verify it was created in the custom tmpDir
43+
require.Contains(t, dbPath, customTmpDir)
44+
})
45+
}
46+
47+
func TestSaveC1z(t *testing.T) {
48+
tmpDir := t.TempDir()
49+
50+
t.Run("save valid db file", func(t *testing.T) {
51+
testData := []byte("test database content for saving")
52+
dbFile := filepath.Join(tmpDir, "save_test.db")
53+
err := os.WriteFile(dbFile, testData, 0600)
54+
require.NoError(t, err)
55+
defer os.Remove(dbFile)
56+
57+
outputFile := filepath.Join(tmpDir, "save_test.c1z")
58+
err = saveC1z(dbFile, outputFile, 1)
59+
require.NoError(t, err)
60+
require.FileExists(t, outputFile)
61+
defer os.Remove(outputFile)
62+
63+
// Verify the file has the correct header
64+
fileData, err := os.ReadFile(outputFile)
65+
require.NoError(t, err)
66+
require.True(t, len(fileData) >= len(C1ZFileHeader))
67+
require.Equal(t, C1ZFileHeader, fileData[:len(C1ZFileHeader)])
68+
69+
// Verify we can decode it
70+
f, err := os.Open(outputFile)
71+
require.NoError(t, err)
72+
defer f.Close()
73+
74+
decoder, err := NewDecoder(f)
75+
require.NoError(t, err)
76+
defer decoder.Close()
77+
78+
decodedData, err := io.ReadAll(decoder)
79+
require.NoError(t, err)
80+
require.Equal(t, testData, decodedData)
81+
})
82+
83+
t.Run("save with empty output path returns error", func(t *testing.T) {
84+
dbFile := filepath.Join(tmpDir, "test.db")
85+
err := os.WriteFile(dbFile, []byte(""), 0600)
86+
require.NoError(t, err)
87+
defer os.Remove(dbFile)
88+
89+
err = saveC1z(dbFile, "", 1)
90+
require.Error(t, err)
91+
require.True(t, status.Code(err) == codes.InvalidArgument)
92+
require.Contains(t, err.Error(), "output file path not configured")
93+
})
94+
95+
t.Run("save with non-existent db file returns error", func(t *testing.T) {
96+
nonExistentDb := filepath.Join(tmpDir, "nonexistent.db")
97+
outputFile := filepath.Join(tmpDir, "output.c1z")
98+
99+
err := saveC1z(nonExistentDb, outputFile, 1)
100+
require.Error(t, err)
101+
})
102+
103+
t.Run("save overwrites existing file", func(t *testing.T) {
104+
testData1 := []byte("first content")
105+
dbFile1 := filepath.Join(tmpDir, "overwrite1.db")
106+
err := os.WriteFile(dbFile1, testData1, 0600)
107+
require.NoError(t, err)
108+
defer os.Remove(dbFile1)
109+
110+
outputFile := filepath.Join(tmpDir, "overwrite.c1z")
111+
err = saveC1z(dbFile1, outputFile, 1)
112+
require.NoError(t, err)
113+
defer os.Remove(outputFile)
114+
115+
// Get the size of the first file
116+
stat1, err := os.Stat(outputFile)
117+
require.NoError(t, err)
118+
size1 := stat1.Size()
119+
120+
// Save different content to the same file
121+
testData2 := []byte("second content - different")
122+
dbFile2 := filepath.Join(tmpDir, "overwrite2.db")
123+
err = os.WriteFile(dbFile2, testData2, 0600)
124+
require.NoError(t, err)
125+
defer os.Remove(dbFile2)
126+
127+
err = saveC1z(dbFile2, outputFile, 1)
128+
require.NoError(t, err)
129+
130+
// Verify the file was overwritten
131+
stat2, err := os.Stat(outputFile)
132+
require.NoError(t, err)
133+
// Size might be different due to compression, but file should exist and be valid
134+
require.NotEqual(t, size1, stat2.Size())
135+
136+
// Verify the content is the new content
137+
f, err := os.Open(outputFile)
138+
require.NoError(t, err)
139+
defer f.Close()
140+
141+
decoder, err := NewDecoder(f)
142+
require.NoError(t, err)
143+
defer decoder.Close()
144+
145+
decodedData, err := io.ReadAll(decoder)
146+
require.NoError(t, err)
147+
require.Equal(t, testData2, decodedData)
148+
})
149+
150+
t.Run("save empty db file", func(t *testing.T) {
151+
emptyDbFile := filepath.Join(tmpDir, "empty.db")
152+
err := os.WriteFile(emptyDbFile, []byte{}, 0600)
153+
require.NoError(t, err)
154+
155+
outputFile := filepath.Join(tmpDir, "empty.c1z")
156+
err = saveC1z(emptyDbFile, outputFile, 1)
157+
require.NoError(t, err)
158+
require.FileExists(t, outputFile)
159+
160+
// Verify the file has the correct header
161+
fileData, err := os.ReadFile(outputFile)
162+
require.NoError(t, err)
163+
require.True(t, len(fileData) >= len(C1ZFileHeader))
164+
require.Equal(t, C1ZFileHeader, fileData[:len(C1ZFileHeader)])
165+
166+
// Verify we can decode it (should be empty)
167+
f, err := os.Open(outputFile)
168+
require.NoError(t, err)
169+
defer f.Close()
170+
171+
decoder, err := NewDecoder(f)
172+
require.NoError(t, err)
173+
defer decoder.Close()
174+
175+
decodedData, err := io.ReadAll(decoder)
176+
require.NoError(t, err)
177+
require.Empty(t, decodedData)
178+
})
179+
}

0 commit comments

Comments
 (0)