Skip to content

Commit 1fa36f5

Browse files
committed
Add next-test command and adapt CLAUDE.md for this repo
- Create cmd/next-test to find the next todo test (shortest query first) - Rewrite CLAUDE.md to match ClickHouse parser workflow: - Tests use explain.txt (not ast.json) - Remove TsqlAstParser section (not applicable) - Document metadata options
1 parent 9b89527 commit 1fa36f5

File tree

2 files changed

+118
-68
lines changed

2 files changed

+118
-68
lines changed

CLAUDE.md

Lines changed: 15 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ This tool finds all tests with `todo: true` in their metadata and returns the on
1313
## Workflow
1414

1515
1. Run `go run ./cmd/next-test` to find the next test to implement
16-
2. Check the test's `query.sql` to understand what SQL needs parsing
17-
3. Check the test's `ast.json` to understand the expected output format
16+
2. Check the test's `query.sql` to understand what ClickHouse SQL needs parsing
17+
3. Check the test's `explain.txt` to understand the expected EXPLAIN output
1818
4. Implement the necessary AST types in `ast/`
1919
5. Add parser logic in `parser/parser.go`
20-
6. Add JSON marshaling functions in `parser/parser.go`
20+
6. Update the `Explain()` function if needed to match ClickHouse's output format
2121
7. Enable the test by removing `todo: true` from its `metadata.json` (set it to `{}`)
2222
8. Run `go test ./parser/... -timeout 5s` to verify
2323
9. Check if other todo tests now pass (see below)
@@ -37,79 +37,26 @@ The tests are very fast. If a test is timing out, it indicates a bug (likely an
3737
After implementing parser changes, run:
3838

3939
```bash
40-
go test ./parser/... -only-todo -v 2>&1 | grep "PASS:"
40+
go test ./parser/... -check-skipped -v 2>&1 | grep "PASSES NOW"
4141
```
4242

43-
This shows any todo tests that now pass. Enable those tests by removing `todo: true` from their `metadata.json`.
44-
45-
Available test flags:
46-
47-
- `-only-todo` - Run only todo/invalid_syntax tests (find newly passing tests)
48-
- `-run-todo` - Run todo/invalid_syntax tests along with normal tests
43+
Tests that output `PASSES NOW` can have their `todo` flag removed from `metadata.json`. This helps identify when parser improvements fix multiple tests at once.
4944

5045
## Test Structure
5146

5247
Each test in `parser/testdata/` contains:
5348

54-
- `metadata.json` - `{}` for enabled tests, `{"todo": true}` for pending tests, or `{"invalid_syntax": true}` for tests with invalid SQL
55-
- `query.sql` - T-SQL to parse
56-
- `ast.json` - Expected AST output
57-
58-
## Important Rules
59-
60-
**NEVER modify `ast.json` files** - These are golden files containing the expected output. If tests fail due to JSON mismatches, fix the Go code to match the expected output, not the other way around.
61-
62-
## Generating ast.json with TsqlAstParser
63-
64-
The `TsqlAstParser/` directory contains a C# tool that generates `ast.json` files using Microsoft's official T-SQL parser (ScriptDom).
65-
66-
### Prerequisites
67-
68-
1. Install .NET 8.0 SDK:
69-
70-
```bash
71-
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 8.0 --install-dir ~/.dotnet
72-
```
73-
74-
2. Download the NuGet package (if `packages/` directory is empty):
75-
76-
```bash
77-
mkdir -p packages
78-
curl -L -o packages/microsoft.sqlserver.transactsql.scriptdom.170.128.0.nupkg \
79-
"https://api.nuget.org/v3-flatcontainer/microsoft.sqlserver.transactsql.scriptdom/170.128.0/microsoft.sqlserver.transactsql.scriptdom.170.128.0.nupkg"
80-
```
81-
82-
3. Build the tool:
83-
84-
```bash
85-
~/.dotnet/dotnet build TsqlAstParser -c Release
86-
```
87-
88-
### Usage
49+
- `metadata.json` - `{}` for enabled tests, `{"todo": true}` for pending tests
50+
- `query.sql` - ClickHouse SQL to parse
51+
- `explain.txt` - Expected EXPLAIN AST output (matches ClickHouse's format)
8952

90-
Generate `ast.json` for a single test:
53+
### Metadata Options
9154

92-
```bash
93-
~/.dotnet/dotnet run --project TsqlAstParser -c Release -- parser/testdata/TestName/query.sql parser/testdata/TestName/ast.json
94-
```
55+
- `todo: true` - Test is pending implementation
56+
- `skip: true` - Skip test entirely (e.g., causes infinite loop)
57+
- `explain: false` - Skip test (e.g., ClickHouse couldn't parse it)
58+
- `parse_error: true` - Query is intentionally invalid SQL
9559

96-
Generate `ast.json` for all tests missing it:
97-
98-
```bash
99-
for dir in parser/testdata/*/; do
100-
if [ -f "$dir/query.sql" ] && [ ! -f "$dir/ast.json" ]; then
101-
~/.dotnet/dotnet run --project TsqlAstParser -c Release -- "$dir/query.sql" "$dir/ast.json"
102-
fi
103-
done
104-
```
105-
106-
### Limitations
107-
108-
TsqlAstParser uses TSql160Parser (SQL Server 2022) and cannot parse:
109-
110-
- SQL Server 170+ features (VECTOR indexes, AI functions, JSON enhancements)
111-
- Fabric DW-specific syntax (CLONE TABLE, CLUSTER BY)
112-
- Deprecated syntax removed in newer versions
113-
- Intentionally invalid SQL (error test cases)
60+
## Important Rules
11461

115-
Tests for unsupported syntax will not have `ast.json` files generated.
62+
**NEVER modify `explain.txt` files** - These are golden files containing the expected output from ClickHouse. If tests fail due to output mismatches, fix the Go code to match the expected output, not the other way around.

cmd/next-test/main.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"sort"
9+
)
10+
11+
type testMetadata struct {
12+
Todo bool `json:"todo,omitempty"`
13+
Explain *bool `json:"explain,omitempty"`
14+
Skip bool `json:"skip,omitempty"`
15+
ParseError bool `json:"parse_error,omitempty"`
16+
}
17+
18+
type todoTest struct {
19+
name string
20+
querySize int
21+
}
22+
23+
func main() {
24+
testdataDir := "parser/testdata"
25+
entries, err := os.ReadDir(testdataDir)
26+
if err != nil {
27+
fmt.Fprintf(os.Stderr, "Error reading testdata: %v\n", err)
28+
os.Exit(1)
29+
}
30+
31+
var todoTests []todoTest
32+
33+
for _, entry := range entries {
34+
if !entry.IsDir() {
35+
continue
36+
}
37+
38+
testDir := filepath.Join(testdataDir, entry.Name())
39+
metadataPath := filepath.Join(testDir, "metadata.json")
40+
41+
// Read metadata
42+
metadataBytes, err := os.ReadFile(metadataPath)
43+
if err != nil {
44+
continue
45+
}
46+
47+
var metadata testMetadata
48+
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
49+
continue
50+
}
51+
52+
// Only include tests marked as todo
53+
if !metadata.Todo {
54+
continue
55+
}
56+
57+
// Skip tests with skip or explain=false or parse_error
58+
if metadata.Skip || (metadata.Explain != nil && !*metadata.Explain) || metadata.ParseError {
59+
continue
60+
}
61+
62+
// Read query to get its size
63+
queryPath := filepath.Join(testDir, "query.sql")
64+
queryBytes, err := os.ReadFile(queryPath)
65+
if err != nil {
66+
continue
67+
}
68+
69+
todoTests = append(todoTests, todoTest{
70+
name: entry.Name(),
71+
querySize: len(queryBytes),
72+
})
73+
}
74+
75+
if len(todoTests) == 0 {
76+
fmt.Println("No todo tests found!")
77+
return
78+
}
79+
80+
// Sort by query size (shortest first)
81+
sort.Slice(todoTests, func(i, j int) bool {
82+
return todoTests[i].querySize < todoTests[j].querySize
83+
})
84+
85+
// Print the shortest one
86+
next := todoTests[0]
87+
testDir := filepath.Join(testdataDir, next.name)
88+
89+
fmt.Printf("Next test: %s\n\n", next.name)
90+
91+
// Print query.sql contents
92+
queryPath := filepath.Join(testDir, "query.sql")
93+
queryBytes, _ := os.ReadFile(queryPath)
94+
fmt.Printf("Query (%d bytes):\n%s\n", next.querySize, string(queryBytes))
95+
96+
// Print explain.txt contents if it exists
97+
explainPath := filepath.Join(testDir, "explain.txt")
98+
if explainBytes, err := os.ReadFile(explainPath); err == nil {
99+
fmt.Printf("\nExpected EXPLAIN output:\n%s\n", string(explainBytes))
100+
}
101+
102+
fmt.Printf("\nRemaining todo tests: %d\n", len(todoTests))
103+
}

0 commit comments

Comments
 (0)