Skip to content

Commit 8670e0a

Browse files
committed
test: add integration tests for PGLite analyzer
Add integration tests that verify the PGLite analyzer works end-to-end: - Mock WASM module that parses CREATE TABLE and SELECT statements - Tests for SELECT *, SELECT with parameters, specific columns - Tests for multiple tables - Tests for analyzer close/cleanup The mock WASM module implements the JSON protocol expected by the analyzer and can parse simple PostgreSQL schemas and queries. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 217ed0a commit 8670e0a

File tree

3 files changed

+593
-0
lines changed

3 files changed

+593
-0
lines changed
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package pglite
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
"runtime"
10+
"testing"
11+
12+
"github.com/sqlc-dev/sqlc/internal/config"
13+
"github.com/sqlc-dev/sqlc/internal/sql/ast"
14+
)
15+
16+
func TestAnalyzerIntegration(t *testing.T) {
17+
// Find the mock WASM file
18+
_, filename, _, ok := runtime.Caller(0)
19+
if !ok {
20+
t.Fatal("could not get current file path")
21+
}
22+
wasmPath := filepath.Join(filepath.Dir(filename), "testdata", "mock_pglite.wasm")
23+
24+
// Check if WASM file exists
25+
wasmData, err := os.ReadFile(wasmPath)
26+
if err != nil {
27+
t.Skipf("mock_pglite.wasm not found (run 'GOOS=wasip1 GOARCH=wasm go build -o mock_pglite.wasm mock_pglite.go' in testdata/): %v", err)
28+
}
29+
30+
// Calculate SHA256
31+
sum := sha256.Sum256(wasmData)
32+
sha := fmt.Sprintf("%x", sum)
33+
34+
cfg := config.PGLite{
35+
URL: "file://" + wasmPath,
36+
SHA256: sha,
37+
}
38+
39+
analyzer := New(cfg)
40+
ctx := context.Background()
41+
defer analyzer.Close(ctx)
42+
43+
migrations := []string{
44+
"CREATE TABLE users (id INTEGER NOT NULL, name TEXT, email TEXT NOT NULL, created_at TIMESTAMP)",
45+
}
46+
47+
// Create a minimal AST node for position tracking
48+
node := &ast.TODO{}
49+
50+
t.Run("simple select star", func(t *testing.T) {
51+
result, err := analyzer.Analyze(ctx, node, "SELECT * FROM users", migrations, nil)
52+
if err != nil {
53+
t.Fatalf("Analyze failed: %v", err)
54+
}
55+
56+
if len(result.Columns) != 4 {
57+
t.Errorf("expected 4 columns, got %d", len(result.Columns))
58+
}
59+
60+
// Check column names and types
61+
expectedCols := []struct {
62+
name string
63+
notNull bool
64+
}{
65+
{"id", true},
66+
{"name", false},
67+
{"email", true},
68+
{"created_at", false},
69+
}
70+
71+
for i, exp := range expectedCols {
72+
if i >= len(result.Columns) {
73+
break
74+
}
75+
col := result.Columns[i]
76+
if col.Name != exp.name {
77+
t.Errorf("column %d: expected name %q, got %q", i, exp.name, col.Name)
78+
}
79+
if col.NotNull != exp.notNull {
80+
t.Errorf("column %d (%s): expected NotNull=%v, got %v", i, exp.name, exp.notNull, col.NotNull)
81+
}
82+
}
83+
})
84+
85+
t.Run("select with parameters", func(t *testing.T) {
86+
result, err := analyzer.Analyze(ctx, node, "SELECT id, name FROM users WHERE id = $1", migrations, nil)
87+
if err != nil {
88+
t.Fatalf("Analyze failed: %v", err)
89+
}
90+
91+
if len(result.Columns) != 2 {
92+
t.Errorf("expected 2 columns, got %d", len(result.Columns))
93+
}
94+
95+
if len(result.Params) != 1 {
96+
t.Errorf("expected 1 parameter, got %d", len(result.Params))
97+
}
98+
99+
if len(result.Params) > 0 {
100+
if result.Params[0].Number != 1 {
101+
t.Errorf("expected param number 1, got %d", result.Params[0].Number)
102+
}
103+
}
104+
})
105+
106+
t.Run("select specific columns", func(t *testing.T) {
107+
result, err := analyzer.Analyze(ctx, node, "SELECT id, email FROM users", migrations, nil)
108+
if err != nil {
109+
t.Fatalf("Analyze failed: %v", err)
110+
}
111+
112+
if len(result.Columns) != 2 {
113+
t.Errorf("expected 2 columns, got %d", len(result.Columns))
114+
}
115+
116+
if len(result.Columns) >= 2 {
117+
if result.Columns[0].Name != "id" {
118+
t.Errorf("expected first column 'id', got %q", result.Columns[0].Name)
119+
}
120+
if result.Columns[1].Name != "email" {
121+
t.Errorf("expected second column 'email', got %q", result.Columns[1].Name)
122+
}
123+
}
124+
})
125+
}
126+
127+
func TestAnalyzerWithMultipleTables(t *testing.T) {
128+
_, filename, _, ok := runtime.Caller(0)
129+
if !ok {
130+
t.Fatal("could not get current file path")
131+
}
132+
wasmPath := filepath.Join(filepath.Dir(filename), "testdata", "mock_pglite.wasm")
133+
134+
wasmData, err := os.ReadFile(wasmPath)
135+
if err != nil {
136+
t.Skipf("mock_pglite.wasm not found: %v", err)
137+
}
138+
139+
sum := sha256.Sum256(wasmData)
140+
sha := fmt.Sprintf("%x", sum)
141+
142+
cfg := config.PGLite{
143+
URL: "file://" + wasmPath,
144+
SHA256: sha,
145+
}
146+
147+
analyzer := New(cfg)
148+
ctx := context.Background()
149+
defer analyzer.Close(ctx)
150+
151+
migrations := []string{
152+
"CREATE TABLE authors (id INTEGER NOT NULL, name TEXT NOT NULL)",
153+
"CREATE TABLE posts (id INTEGER NOT NULL, author_id INTEGER NOT NULL, title TEXT, body TEXT)",
154+
}
155+
156+
node := &ast.TODO{}
157+
158+
t.Run("query authors table", func(t *testing.T) {
159+
result, err := analyzer.Analyze(ctx, node, "SELECT * FROM authors", migrations, nil)
160+
if err != nil {
161+
t.Fatalf("Analyze failed: %v", err)
162+
}
163+
164+
if len(result.Columns) != 2 {
165+
t.Errorf("expected 2 columns, got %d", len(result.Columns))
166+
}
167+
})
168+
169+
t.Run("query posts table", func(t *testing.T) {
170+
result, err := analyzer.Analyze(ctx, node, "SELECT * FROM posts", migrations, nil)
171+
if err != nil {
172+
t.Fatalf("Analyze failed: %v", err)
173+
}
174+
175+
if len(result.Columns) != 4 {
176+
t.Errorf("expected 4 columns, got %d", len(result.Columns))
177+
}
178+
})
179+
}
180+
181+
func TestAnalyzerClose(t *testing.T) {
182+
_, filename, _, ok := runtime.Caller(0)
183+
if !ok {
184+
t.Fatal("could not get current file path")
185+
}
186+
wasmPath := filepath.Join(filepath.Dir(filename), "testdata", "mock_pglite.wasm")
187+
188+
if _, err := os.Stat(wasmPath); os.IsNotExist(err) {
189+
t.Skipf("mock_pglite.wasm not found: %v", err)
190+
}
191+
192+
wasmData, _ := os.ReadFile(wasmPath)
193+
sum := sha256.Sum256(wasmData)
194+
sha := fmt.Sprintf("%x", sum)
195+
196+
cfg := config.PGLite{
197+
URL: "file://" + wasmPath,
198+
SHA256: sha,
199+
}
200+
201+
analyzer := New(cfg)
202+
ctx := context.Background()
203+
204+
// Close without using should not error
205+
err := analyzer.Close(ctx)
206+
if err != nil {
207+
t.Errorf("Close failed: %v", err)
208+
}
209+
210+
// Double close should not error
211+
err = analyzer.Close(ctx)
212+
if err != nil {
213+
t.Errorf("Double close failed: %v", err)
214+
}
215+
}

0 commit comments

Comments
 (0)