Skip to content

Commit 0bd91eb

Browse files
ethanyzhangclaude
andcommitted
test: add tests for genconfig, queryplan, round; raise coverage to 50%
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 156853f commit 0bd91eb

File tree

6 files changed

+546
-2
lines changed

6 files changed

+546
-2
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
run: |
5656
COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
5757
echo "Total coverage: ${COVERAGE}%"
58-
THRESHOLD=35
58+
THRESHOLD=50
5959
if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then
6060
echo "Coverage ${COVERAGE}% is below threshold ${THRESHOLD}%"
6161
exit 1

cmd/genconfig/main_test.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package genconfig
2+
3+
import (
4+
"io"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
// TestGeneratedConfigsMatch runs genconfig with the real clusters directory
14+
// and verifies the output matches the checked-in golden files.
15+
func TestGeneratedConfigsMatch(t *testing.T) {
16+
clustersDir, err := filepath.Abs("../../clusters")
17+
require.NoError(t, err)
18+
19+
// Snapshot all non-config.json files in cluster subdirectories.
20+
golden := snapshotClusters(t, clustersDir)
21+
require.NotEmpty(t, golden, "no golden files found in clusters/")
22+
23+
// Configure and run genconfig.
24+
TemplatePath = filepath.Join(clustersDir, "templates")
25+
ParameterPath = filepath.Join(clustersDir, "params.json")
26+
Run(nil, []string{clustersDir})
27+
28+
// Compare regenerated files against golden snapshots.
29+
for relPath, expected := range golden {
30+
actual, readErr := os.ReadFile(filepath.Join(clustersDir, relPath))
31+
require.NoError(t, readErr, "failed to read regenerated file %s", relPath)
32+
assert.Equal(t, string(expected), string(actual),
33+
"generated output differs from checked-in golden file: %s", relPath)
34+
}
35+
36+
// Check no unexpected extra files were produced.
37+
regenerated := snapshotClusters(t, clustersDir)
38+
for relPath := range regenerated {
39+
assert.Contains(t, golden, relPath,
40+
"regeneration produced unexpected file: %s", relPath)
41+
}
42+
}
43+
44+
func TestCalculate(t *testing.T) {
45+
cfg := &ClusterConfig{
46+
Name: "test",
47+
MemoryPerNodeGb: 62,
48+
NumberOfWorkers: 4,
49+
VCPUPerWorker: 8,
50+
GeneratorParameters: DefaultGeneratorParameters,
51+
}
52+
cfg.Calculate()
53+
54+
assert.True(t, cfg.ContainerMemoryGb > 0, "ContainerMemoryGb should be positive")
55+
assert.True(t, cfg.HeapSizeGb > 0, "HeapSizeGb should be positive")
56+
assert.True(t, cfg.HeapSizeGb < cfg.ContainerMemoryGb, "HeapSizeGb should be less than ContainerMemoryGb")
57+
assert.True(t, cfg.JavaQueryMaxTotalMemPerNodeGb > 0)
58+
assert.True(t, cfg.JavaQueryMaxMemPerNodeGb > 0)
59+
assert.True(t, cfg.NativeSystemMemGb > 0)
60+
assert.True(t, cfg.NativeQueryMemGb > 0)
61+
assert.True(t, cfg.JoinMaxBroadcastTableSizeMb > 0)
62+
}
63+
64+
// snapshotClusters reads all generated files (non-config.json) in cluster
65+
// subdirectories and returns a map of relative path -> contents.
66+
func snapshotClusters(t *testing.T, clustersDir string) map[string][]byte {
67+
t.Helper()
68+
files := make(map[string][]byte)
69+
70+
entries, err := os.ReadDir(clustersDir)
71+
require.NoError(t, err)
72+
73+
for _, entry := range entries {
74+
if !entry.IsDir() {
75+
continue
76+
}
77+
// Skip non-cluster directories (templates, shared, catalog).
78+
subDir := filepath.Join(clustersDir, entry.Name())
79+
if !hasConfigJson(subDir) {
80+
continue
81+
}
82+
83+
err := filepath.Walk(subDir, func(path string, info os.FileInfo, walkErr error) error {
84+
if walkErr != nil {
85+
return walkErr
86+
}
87+
if info.IsDir() || info.Name() == "config.json" {
88+
return nil
89+
}
90+
rel, relErr := filepath.Rel(clustersDir, path)
91+
if relErr != nil {
92+
return relErr
93+
}
94+
data, readErr := os.ReadFile(path)
95+
if readErr != nil {
96+
return readErr
97+
}
98+
files[rel] = data
99+
return nil
100+
})
101+
require.NoError(t, err)
102+
}
103+
return files
104+
}
105+
106+
func hasConfigJson(dir string) bool {
107+
_, err := os.Stat(filepath.Join(dir, "config.json"))
108+
return err == nil
109+
}
110+
111+
func TestPrintDefaultParams(t *testing.T) {
112+
// Capture stdout by redirecting it to a pipe.
113+
oldStdout := os.Stdout
114+
r, w, err := os.Pipe()
115+
require.NoError(t, err)
116+
os.Stdout = w
117+
118+
PrintDefaultParams(nil, nil)
119+
120+
w.Close()
121+
os.Stdout = oldStdout
122+
out, err := io.ReadAll(r)
123+
require.NoError(t, err)
124+
125+
assert.Contains(t, string(out), "sys_reserved_mem_cap_gb")
126+
}
127+
128+
func TestRunWithBuiltinTemplates(t *testing.T) {
129+
tmpDir := t.TempDir()
130+
clusterDir := filepath.Join(tmpDir, "test-cluster")
131+
require.NoError(t, os.MkdirAll(clusterDir, 0755))
132+
133+
// Write a minimal config.json.
134+
configJSON := `{
135+
"cluster_size": "test",
136+
"worker_instance_type": "r6i.xlarge",
137+
"number_of_workers": 2,
138+
"memory_per_node_gb": 30,
139+
"vcpu_per_worker": 4,
140+
"fragment_result_cache_enabled": false,
141+
"data_cache_enabled": false
142+
}`
143+
require.NoError(t, os.WriteFile(filepath.Join(clusterDir, "config.json"), []byte(configJSON), 0644))
144+
145+
// Run with builtin templates (no -t flag).
146+
TemplatePath = ""
147+
ParameterPath = ""
148+
Run(nil, []string{tmpDir})
149+
150+
// Verify some output was generated.
151+
entries, err := os.ReadDir(clusterDir)
152+
require.NoError(t, err)
153+
// Should have config.json + generated files.
154+
assert.Greater(t, len(entries), 1, "expected generated files in output directory")
155+
}

cmd/genddl/main_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,155 @@ func snapshotDir(t *testing.T, dir string) map[string][]byte {
6868
require.NoError(t, err)
6969
return files
7070
}
71+
72+
func TestIntScaleFactor(t *testing.T) {
73+
tests := []struct {
74+
input string
75+
expected int
76+
}{
77+
{"1", 1},
78+
{"10", 10},
79+
{"1000", 1000},
80+
{"1k", 1000},
81+
{"10k", 10000},
82+
{"abc", 0},
83+
{"k", 0},
84+
}
85+
for _, tc := range tests {
86+
s := &Schema{ScaleFactor: tc.input}
87+
assert.Equal(t, tc.expected, s.intScaleFactor(), "intScaleFactor(%q)", tc.input)
88+
}
89+
}
90+
91+
func TestLoadSchemas(t *testing.T) {
92+
data := []byte(`{"scale_factor":"10","file_format":"parquet","compression_method":"uncompressed"}`)
93+
schemas, err := loadSchemas(data)
94+
require.NoError(t, err)
95+
require.Len(t, schemas, 4, "should produce 4 schema variants")
96+
97+
// Verify the 4 combinations.
98+
assert.True(t, schemas[0].Iceberg && !schemas[0].Partitioned)
99+
assert.True(t, schemas[1].Iceberg && schemas[1].Partitioned)
100+
assert.True(t, !schemas[2].Iceberg && !schemas[2].Partitioned)
101+
assert.True(t, !schemas[3].Iceberg && schemas[3].Partitioned)
102+
103+
// Each schema should have independent maps.
104+
schemas[0].Tables["foo"] = &Table{Name: "foo"}
105+
assert.Empty(t, schemas[1].Tables, "schema variants should not share Tables map")
106+
}
107+
108+
func TestLoadSchemasDefaults(t *testing.T) {
109+
data := []byte(`{"scale_factor":"1","file_format":"orc","compression_method":"zstd"}`)
110+
schemas, err := loadSchemas(data)
111+
require.NoError(t, err)
112+
113+
// Verify defaults are applied.
114+
assert.Equal(t, "tpcds", schemas[0].Workload)
115+
assert.Equal(t, "tpc-ds", schemas[0].WorkloadDefinition)
116+
117+
// Verify zstd compression session vars.
118+
assert.Equal(t, "ZSTD", schemas[0].SessionVariables["iceberg.compression_codec"])
119+
assert.Equal(t, "ZSTD", schemas[3].SessionVariables["hive.compression_codec"])
120+
}
121+
122+
func TestGetNamedOutput(t *testing.T) {
123+
tests := []struct {
124+
name string
125+
data string
126+
workload string
127+
expected string
128+
}{
129+
{
130+
"uncompressed",
131+
`{"scale_factor":"10","file_format":"parquet","compression_method":"uncompressed"}`,
132+
"tpcds",
133+
"tpcds-sf10-parquet",
134+
},
135+
{
136+
"zstd",
137+
`{"scale_factor":"100","file_format":"orc","compression_method":"zstd"}`,
138+
"tpch",
139+
"tpch-sf100-orc-zstd",
140+
},
141+
}
142+
for _, tc := range tests {
143+
t.Run(tc.name, func(t *testing.T) {
144+
result, err := getNamedOutput([]byte(tc.data), tc.workload)
145+
require.NoError(t, err)
146+
assert.Equal(t, tc.expected, result)
147+
})
148+
}
149+
}
150+
151+
func TestCleanOutputDir(t *testing.T) {
152+
dir := t.TempDir()
153+
154+
// Create some files.
155+
require.NoError(t, os.WriteFile(filepath.Join(dir, "a.sql"), []byte("test"), 0644))
156+
require.NoError(t, os.WriteFile(filepath.Join(dir, "b.sh"), []byte("test"), 0644))
157+
158+
// Create a subdirectory (should not be deleted).
159+
require.NoError(t, os.MkdirAll(filepath.Join(dir, "subdir"), 0755))
160+
161+
err := cleanOutputDir(dir)
162+
require.NoError(t, err)
163+
164+
entries, err := os.ReadDir(dir)
165+
require.NoError(t, err)
166+
assert.Len(t, entries, 1, "only subdirectory should remain")
167+
assert.Equal(t, "subdir", entries[0].Name())
168+
}
169+
170+
func TestCleanOutputDirNonExistent(t *testing.T) {
171+
err := cleanOutputDir("/nonexistent/path")
172+
assert.NoError(t, err, "should return nil for non-existent directory")
173+
}
174+
175+
func TestIsPartitioned(t *testing.T) {
176+
// Table with explicit PartitionedMinScale.
177+
tbl := &Table{PartitionedMinScale: 100}
178+
assert.False(t, tbl.isPartitioned(10))
179+
assert.True(t, tbl.isPartitioned(100))
180+
assert.True(t, tbl.isPartitioned(1000))
181+
182+
// Table without PartitionedMinScale uses Partitioned field.
183+
tbl2 := &Table{Partitioned: true}
184+
assert.True(t, tbl2.isPartitioned(1))
185+
186+
tbl3 := &Table{Partitioned: false}
187+
assert.False(t, tbl3.isPartitioned(1))
188+
}
189+
190+
func TestInitIsVarchar(t *testing.T) {
191+
varcharType := "VARCHAR(100)"
192+
intType := "INT"
193+
194+
tbl := &Table{
195+
Columns: []*Column{
196+
{Name: "a", Type: &varcharType},
197+
{Name: "b", Type: &intType},
198+
{Name: "c", Type: nil},
199+
},
200+
}
201+
tbl.initIsVarchar()
202+
203+
assert.True(t, tbl.Columns[0].IsVarchar)
204+
assert.False(t, tbl.Columns[1].IsVarchar)
205+
assert.False(t, tbl.Columns[2].IsVarchar)
206+
}
207+
208+
func TestSetNames(t *testing.T) {
209+
s := &Schema{
210+
ScaleFactor: "100",
211+
FileFormat: "parquet",
212+
CompressionMethod: "zstd",
213+
Workload: "tpcds",
214+
Iceberg: true,
215+
Partitioned: true,
216+
}
217+
s.setNames()
218+
219+
assert.Equal(t, "tpcds_sf100_parquet_partitioned_iceberg_zstd", s.SchemaName)
220+
assert.Equal(t, "tpcds-sf100-parquet-partitioned-iceberg-zstd", s.LocationName)
221+
assert.Equal(t, "tpcds_sf100_parquet_partitioned_iceberg", s.UncompressedName)
222+
}

0 commit comments

Comments
 (0)