@sqldoc/templates 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -8
- package/src/__tests__/dedent.test.ts +0 -45
- package/src/__tests__/docker-templates.test.ts +0 -134
- package/src/__tests__/go-structs.test.ts +0 -184
- package/src/__tests__/naming.test.ts +0 -48
- package/src/__tests__/python-dataclasses.test.ts +0 -185
- package/src/__tests__/rust-structs.test.ts +0 -176
- package/src/__tests__/tags-helpers.test.ts +0 -72
- package/src/__tests__/type-mapping.test.ts +0 -332
- package/src/__tests__/typescript.test.ts +0 -202
- package/src/cobol-copybook/test/.gitignore +0 -6
- package/src/cobol-copybook/test/Dockerfile +0 -7
- package/src/csharp-records/test/.gitignore +0 -6
- package/src/csharp-records/test/Dockerfile +0 -6
- package/src/diesel/test/.gitignore +0 -6
- package/src/diesel/test/Dockerfile +0 -16
- package/src/drizzle/test/.gitignore +0 -6
- package/src/drizzle/test/Dockerfile +0 -8
- package/src/drizzle/test/test.ts +0 -71
- package/src/efcore/test/.gitignore +0 -6
- package/src/efcore/test/Dockerfile +0 -7
- package/src/go-structs/test/.gitignore +0 -6
- package/src/go-structs/test/Dockerfile +0 -13
- package/src/go-structs/test/test.go +0 -71
- package/src/gorm/test/.gitignore +0 -6
- package/src/gorm/test/Dockerfile +0 -13
- package/src/gorm/test/test.go +0 -65
- package/src/java-records/test/.gitignore +0 -6
- package/src/java-records/test/Dockerfile +0 -11
- package/src/java-records/test/Test.java +0 -93
- package/src/jpa/test/.gitignore +0 -6
- package/src/jpa/test/Dockerfile +0 -14
- package/src/jpa/test/Test.java +0 -111
- package/src/json-schema/test/.gitignore +0 -6
- package/src/json-schema/test/Dockerfile +0 -18
- package/src/knex/test/.gitignore +0 -6
- package/src/knex/test/Dockerfile +0 -7
- package/src/knex/test/test.ts +0 -75
- package/src/kotlin-data/test/.gitignore +0 -6
- package/src/kotlin-data/test/Dockerfile +0 -14
- package/src/kotlin-data/test/Test.kt +0 -82
- package/src/kysely/test/.gitignore +0 -6
- package/src/kysely/test/Dockerfile +0 -8
- package/src/kysely/test/test.ts +0 -82
- package/src/prisma/test/.gitignore +0 -6
- package/src/prisma/test/Dockerfile +0 -7
- package/src/protobuf/test/.gitignore +0 -6
- package/src/protobuf/test/Dockerfile +0 -6
- package/src/pydantic/test/.gitignore +0 -6
- package/src/pydantic/test/Dockerfile +0 -8
- package/src/pydantic/test/test.py +0 -63
- package/src/python-dataclasses/test/.gitignore +0 -6
- package/src/python-dataclasses/test/Dockerfile +0 -8
- package/src/python-dataclasses/test/test.py +0 -63
- package/src/rust-structs/test/.gitignore +0 -6
- package/src/rust-structs/test/Dockerfile +0 -22
- package/src/rust-structs/test/test.rs +0 -82
- package/src/sqlalchemy/test/.gitignore +0 -6
- package/src/sqlalchemy/test/Dockerfile +0 -8
- package/src/sqlalchemy/test/test.py +0 -61
- package/src/sqlc/test/.gitignore +0 -6
- package/src/sqlc/test/Dockerfile +0 -13
- package/src/sqlc/test/test.go +0 -91
- package/src/typescript/test/.gitignore +0 -6
- package/src/typescript/test/Dockerfile +0 -8
- package/src/typescript/test/test.ts +0 -89
- package/src/xsd/test/.gitignore +0 -6
- package/src/xsd/test/Dockerfile +0 -6
- package/src/zod/test/.gitignore +0 -6
- package/src/zod/test/Dockerfile +0 -6
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import typescript from '../typescript/index.ts'
|
|
3
|
-
|
|
4
|
-
const generate = typescript.generate
|
|
5
|
-
|
|
6
|
-
import type { AtlasRealm } from '@sqldoc/atlas'
|
|
7
|
-
import type { TemplateContext } from '@sqldoc/ns-codegen'
|
|
8
|
-
|
|
9
|
-
const testRealm: AtlasRealm = {
|
|
10
|
-
schemas: [
|
|
11
|
-
{
|
|
12
|
-
name: 'public',
|
|
13
|
-
tables: [
|
|
14
|
-
{
|
|
15
|
-
name: 'users',
|
|
16
|
-
columns: [
|
|
17
|
-
{ name: 'id', type: { T: 'bigserial', null: false, category: 'integer' } },
|
|
18
|
-
{ name: 'email', type: { T: 'character varying', raw: 'varchar(255)', null: false, category: 'string' } },
|
|
19
|
-
{ name: 'name', type: { T: 'text', null: true, category: 'string' } },
|
|
20
|
-
{ name: 'age', type: { T: 'integer', null: true, category: 'integer' } },
|
|
21
|
-
{ name: 'is_active', type: { T: 'boolean', null: false, category: 'boolean' } },
|
|
22
|
-
{ name: 'metadata', type: { T: 'jsonb', null: true, category: 'json' } },
|
|
23
|
-
{ name: 'created_at', type: { T: 'timestamp with time zone', null: false, category: 'time' } },
|
|
24
|
-
{ name: 'tags', type: { T: 'text[]', null: true, category: 'array' } },
|
|
25
|
-
{ name: 'avatar', type: { T: 'bytea', null: true, category: 'binary' } },
|
|
26
|
-
{ name: 'balance', type: { T: 'numeric(10,2)', null: true, category: 'decimal' } },
|
|
27
|
-
{ name: 'external_id', type: { T: 'uuid', null: true, category: 'uuid' } },
|
|
28
|
-
],
|
|
29
|
-
primary_key: { parts: [{ column: 'id' }] },
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: 'posts',
|
|
33
|
-
columns: [
|
|
34
|
-
{ name: 'id', type: { T: 'bigserial', null: false, category: 'integer' } },
|
|
35
|
-
{ name: 'user_id', type: { T: 'bigint', null: false, category: 'integer' } },
|
|
36
|
-
{ name: 'title', type: { T: 'text', null: false, category: 'string' } },
|
|
37
|
-
{ name: 'body', type: { T: 'text', null: false, category: 'string' } },
|
|
38
|
-
{ name: 'published_at', type: { T: 'timestamp with time zone', null: true, category: 'time' } },
|
|
39
|
-
{ name: 'view_count', type: { T: 'integer', null: false, category: 'integer' } },
|
|
40
|
-
{ name: 'rating', type: { T: 'double precision', null: true, category: 'float' } },
|
|
41
|
-
],
|
|
42
|
-
primary_key: { parts: [{ column: 'id' }] },
|
|
43
|
-
foreign_keys: [
|
|
44
|
-
{ symbol: 'posts_user_id_fkey', columns: ['user_id'], ref_table: 'users', ref_columns: ['id'] },
|
|
45
|
-
],
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'comments',
|
|
49
|
-
columns: [
|
|
50
|
-
{ name: 'id', type: { T: 'bigserial', null: false, category: 'integer' } },
|
|
51
|
-
{ name: 'post_id', type: { T: 'bigint', null: false, category: 'integer' } },
|
|
52
|
-
{ name: 'user_id', type: { T: 'bigint', null: false, category: 'integer' } },
|
|
53
|
-
{ name: 'content', type: { T: 'text', null: false, category: 'string' } },
|
|
54
|
-
{ name: 'created_at', type: { T: 'timestamp with time zone', null: false, category: 'time' } },
|
|
55
|
-
],
|
|
56
|
-
primary_key: { parts: [{ column: 'id' }] },
|
|
57
|
-
foreign_keys: [
|
|
58
|
-
{ symbol: 'comments_post_id_fkey', columns: ['post_id'], ref_table: 'posts', ref_columns: ['id'] },
|
|
59
|
-
{ symbol: 'comments_user_id_fkey', columns: ['user_id'], ref_table: 'users', ref_columns: ['id'] },
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function makeCtx(overrides?: Partial<TemplateContext>): TemplateContext {
|
|
68
|
-
return {
|
|
69
|
-
realm: testRealm,
|
|
70
|
-
allFileTags: [],
|
|
71
|
-
docsMeta: [],
|
|
72
|
-
config: {},
|
|
73
|
-
output: './out',
|
|
74
|
-
templateName: 'typescript',
|
|
75
|
-
...overrides,
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
describe('typescript template', () => {
|
|
80
|
-
it('generates interfaces for all tables', () => {
|
|
81
|
-
const result = generate(makeCtx())
|
|
82
|
-
expect(result.files).toHaveLength(1)
|
|
83
|
-
expect(result.files[0].path).toBe('models.ts')
|
|
84
|
-
|
|
85
|
-
const content = result.files[0].content
|
|
86
|
-
expect(content).toContain('export interface Users {')
|
|
87
|
-
expect(content).toContain('export interface Posts {')
|
|
88
|
-
expect(content).toContain('export interface Comments {')
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('maps bigserial to number', () => {
|
|
92
|
-
const result = generate(makeCtx())
|
|
93
|
-
const content = result.files[0].content
|
|
94
|
-
expect(content).toContain('id: number')
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('uses optional (?) for nullable columns by default', () => {
|
|
98
|
-
const result = generate(makeCtx())
|
|
99
|
-
const content = result.files[0].content
|
|
100
|
-
expect(content).toContain('name?: string')
|
|
101
|
-
expect(content).toContain('age?: number')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('uses null-union style when configured', () => {
|
|
105
|
-
const result = generate(makeCtx({ config: { nullableStyle: 'null-union' } }))
|
|
106
|
-
const content = result.files[0].content
|
|
107
|
-
expect(content).toContain('name: string | null')
|
|
108
|
-
expect(content).toContain('age: number | null')
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('maps text[] to string[] array type', () => {
|
|
112
|
-
const result = generate(makeCtx())
|
|
113
|
-
const content = result.files[0].content
|
|
114
|
-
expect(content).toContain('tags?: string[]')
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it('maps jsonb to unknown', () => {
|
|
118
|
-
const result = generate(makeCtx())
|
|
119
|
-
const content = result.files[0].content
|
|
120
|
-
expect(content).toContain('metadata?: unknown')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('maps bytea to Buffer', () => {
|
|
124
|
-
const result = generate(makeCtx())
|
|
125
|
-
const content = result.files[0].content
|
|
126
|
-
expect(content).toContain('avatar?: Buffer')
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('maps uuid to string', () => {
|
|
130
|
-
const result = generate(makeCtx())
|
|
131
|
-
const content = result.files[0].content
|
|
132
|
-
expect(content).toContain('externalId?: string')
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('includes header comment', () => {
|
|
136
|
-
const result = generate(makeCtx())
|
|
137
|
-
const content = result.files[0].content
|
|
138
|
-
expect(content).toContain('// Generated by @sqldoc/templates/typescript -- DO NOT EDIT')
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('respects @codegen.skip tag', () => {
|
|
142
|
-
const ctx = makeCtx({
|
|
143
|
-
allFileTags: [
|
|
144
|
-
{
|
|
145
|
-
sourceFile: 'test.sql',
|
|
146
|
-
objects: [
|
|
147
|
-
{
|
|
148
|
-
objectName: 'comments',
|
|
149
|
-
target: 'table',
|
|
150
|
-
tags: [{ namespace: 'codegen', tag: 'skip', args: [] }],
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
})
|
|
156
|
-
const result = generate(ctx)
|
|
157
|
-
const content = result.files[0].content
|
|
158
|
-
expect(content).not.toContain('export interface Comments')
|
|
159
|
-
expect(content).toContain('export interface Users')
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('respects @codegen.rename tag', () => {
|
|
163
|
-
const ctx = makeCtx({
|
|
164
|
-
allFileTags: [
|
|
165
|
-
{
|
|
166
|
-
sourceFile: 'test.sql',
|
|
167
|
-
objects: [
|
|
168
|
-
{
|
|
169
|
-
objectName: 'users',
|
|
170
|
-
target: 'table',
|
|
171
|
-
tags: [{ namespace: 'codegen', tag: 'rename', args: ['Account'] }],
|
|
172
|
-
},
|
|
173
|
-
],
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
})
|
|
177
|
-
const result = generate(ctx)
|
|
178
|
-
const content = result.files[0].content
|
|
179
|
-
expect(content).toContain('export interface Account {')
|
|
180
|
-
expect(content).not.toContain('export interface Users {')
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
it('respects @codegen.type override on column', () => {
|
|
184
|
-
const ctx = makeCtx({
|
|
185
|
-
allFileTags: [
|
|
186
|
-
{
|
|
187
|
-
sourceFile: 'test.sql',
|
|
188
|
-
objects: [
|
|
189
|
-
{
|
|
190
|
-
objectName: 'users.metadata',
|
|
191
|
-
target: 'column',
|
|
192
|
-
tags: [{ namespace: 'codegen', tag: 'type', args: ['Record<string, any>'] }],
|
|
193
|
-
},
|
|
194
|
-
],
|
|
195
|
-
},
|
|
196
|
-
],
|
|
197
|
-
})
|
|
198
|
-
const result = generate(ctx)
|
|
199
|
-
const content = result.files[0].content
|
|
200
|
-
expect(content).toContain('metadata?: Record<string, any>')
|
|
201
|
-
})
|
|
202
|
-
})
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
FROM debian:bookworm-slim
|
|
2
|
-
RUN apt-get update && apt-get install -y --no-install-recommends gnucobol4 && rm -rf /var/lib/apt/lists/*
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
COPY . .
|
|
5
|
-
RUN printf ' IDENTIFICATION DIVISION.\n PROGRAM-ID. TEST-CPY.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY "schema.cpy".\n PROCEDURE DIVISION.\n STOP RUN.\n' > test.cob
|
|
6
|
-
RUN cobc -fsyntax-only test.cob
|
|
7
|
-
CMD ["echo", "ok"]
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
FROM rust:1.85-slim
|
|
2
|
-
WORKDIR /app
|
|
3
|
-
RUN cargo init --name typecheck .
|
|
4
|
-
RUN cat >> Cargo.toml <<'TOML'
|
|
5
|
-
diesel = { version = "2", features = ["postgres"] }
|
|
6
|
-
serde = { version = "1", features = ["derive"] }
|
|
7
|
-
serde_json = "1"
|
|
8
|
-
chrono = { version = "0.4", features = ["serde"] }
|
|
9
|
-
uuid = { version = "1", features = ["serde"] }
|
|
10
|
-
bigdecimal = { version = "0.4", features = ["serde"] }
|
|
11
|
-
TOML
|
|
12
|
-
RUN rm src/main.rs
|
|
13
|
-
COPY . src/
|
|
14
|
-
RUN echo 'mod schema;' > src/lib.rs && echo 'mod models;' >> src/lib.rs
|
|
15
|
-
RUN cargo check
|
|
16
|
-
CMD ["echo", "ok"]
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
FROM node:23-slim
|
|
2
|
-
WORKDIR /app
|
|
3
|
-
COPY . .
|
|
4
|
-
RUN npm init -y && npm pkg set type=module && npm install typescript@5 drizzle-orm@0.38 pg @types/pg @types/node --save-dev
|
|
5
|
-
# Step 1: typecheck the generated schema
|
|
6
|
-
RUN npx tsc --noEmit --strict --esModuleInterop --module nodenext --moduleResolution nodenext --allowImportingTsExtensions --skipLibCheck *.ts
|
|
7
|
-
# Step 2: run integration test against real DB
|
|
8
|
-
CMD ["node", "--experimental-strip-types", "test.ts"]
|
package/src/drizzle/test/test.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration test for @sqldoc/templates/drizzle
|
|
3
|
-
* Connects to real Postgres via drizzle-orm, verifies generated schema works.
|
|
4
|
-
*/
|
|
5
|
-
import { drizzle } from 'drizzle-orm/node-postgres'
|
|
6
|
-
import { eq } from 'drizzle-orm'
|
|
7
|
-
import pg from 'pg'
|
|
8
|
-
import * as schema from './schema.ts'
|
|
9
|
-
|
|
10
|
-
const DATABASE_URL = process.env.DATABASE_URL
|
|
11
|
-
if (!DATABASE_URL) {
|
|
12
|
-
console.error('DATABASE_URL not set')
|
|
13
|
-
process.exit(1)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const pool = new pg.Pool({ connectionString: DATABASE_URL })
|
|
17
|
-
const db = drizzle(pool, { schema })
|
|
18
|
-
|
|
19
|
-
let failed = 0
|
|
20
|
-
function assert(condition: boolean, msg: string) {
|
|
21
|
-
if (!condition) {
|
|
22
|
-
console.error(`FAIL: ${msg}`)
|
|
23
|
-
failed++
|
|
24
|
-
} else {
|
|
25
|
-
console.log(` ok: ${msg}`)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function run() {
|
|
30
|
-
try {
|
|
31
|
-
console.log('--- drizzle integration test ---')
|
|
32
|
-
|
|
33
|
-
// 1. Query known seeded user
|
|
34
|
-
const users = await db.select().from(schema.users).where(eq(schema.users.id, 1))
|
|
35
|
-
assert(users.length === 1, 'seeded user found')
|
|
36
|
-
assert(users[0].email === 'test@example.com', 'user email matches')
|
|
37
|
-
assert(users[0].name === 'Test User', 'user name matches')
|
|
38
|
-
|
|
39
|
-
// 2. Query known seeded post
|
|
40
|
-
const posts = await db.select().from(schema.posts).where(eq(schema.posts.id, 1))
|
|
41
|
-
assert(posts.length === 1, 'seeded post found')
|
|
42
|
-
assert(posts[0].title === 'Hello World', 'post title matches')
|
|
43
|
-
|
|
44
|
-
// 3. Insert a new post
|
|
45
|
-
await db.insert(schema.posts).values({
|
|
46
|
-
userId: 1,
|
|
47
|
-
title: 'Post from drizzle',
|
|
48
|
-
body: 'test body',
|
|
49
|
-
viewCount: 0,
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
// 4. Read it back
|
|
53
|
-
const newPosts = await db.select().from(schema.posts).where(eq(schema.posts.title, 'Post from drizzle'))
|
|
54
|
-
assert(newPosts.length === 1, 'inserted post found')
|
|
55
|
-
assert(newPosts[0].title === 'Post from drizzle', 'inserted post title matches')
|
|
56
|
-
assert(Number(newPosts[0].userId) === 1, 'inserted post user_id matches')
|
|
57
|
-
|
|
58
|
-
if (failed > 0) {
|
|
59
|
-
console.error(`\n${failed} assertion(s) failed`)
|
|
60
|
-
process.exit(1)
|
|
61
|
-
}
|
|
62
|
-
console.log('\nAll assertions passed!')
|
|
63
|
-
} finally {
|
|
64
|
-
await pool.end()
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
run().catch((err) => {
|
|
69
|
-
console.error(err)
|
|
70
|
-
process.exit(1)
|
|
71
|
-
})
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine
|
|
2
|
-
WORKDIR /app
|
|
3
|
-
RUN dotnet new classlib -n TypeCheck --force && rm TypeCheck/Class1.cs
|
|
4
|
-
RUN dotnet add TypeCheck/ package Microsoft.EntityFrameworkCore --version 9.0.0
|
|
5
|
-
COPY *.cs TypeCheck/
|
|
6
|
-
RUN dotnet build TypeCheck/
|
|
7
|
-
CMD ["echo", "ok"]
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
FROM golang:1.24-alpine
|
|
2
|
-
WORKDIR /app
|
|
3
|
-
# Set up Go module with models as a sub-package
|
|
4
|
-
RUN go mod init sqldoc-test
|
|
5
|
-
RUN mkdir -p models cmd/test
|
|
6
|
-
COPY models.go models/
|
|
7
|
-
COPY test.go cmd/test/main.go
|
|
8
|
-
ENV GOTOOLCHAIN=auto
|
|
9
|
-
RUN go get github.com/jackc/pgx/v5 && go mod tidy
|
|
10
|
-
# Step 1: typecheck/compile all packages
|
|
11
|
-
RUN go build -o /usr/local/bin/test-runner ./cmd/test
|
|
12
|
-
# Step 2: run integration test against real DB
|
|
13
|
-
CMD ["test-runner"]
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"fmt"
|
|
6
|
-
"os"
|
|
7
|
-
|
|
8
|
-
"github.com/jackc/pgx/v5"
|
|
9
|
-
|
|
10
|
-
"sqldoc-test/models"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
var failed int
|
|
14
|
-
|
|
15
|
-
func assert(condition bool, msg string) {
|
|
16
|
-
if !condition {
|
|
17
|
-
fmt.Fprintf(os.Stderr, "FAIL: %s\n", msg)
|
|
18
|
-
failed++
|
|
19
|
-
} else {
|
|
20
|
-
fmt.Printf(" ok: %s\n", msg)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
func main() {
|
|
25
|
-
ctx := context.Background()
|
|
26
|
-
dbURL := os.Getenv("DATABASE_URL")
|
|
27
|
-
if dbURL == "" {
|
|
28
|
-
fmt.Fprintln(os.Stderr, "DATABASE_URL not set")
|
|
29
|
-
os.Exit(1)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
conn, err := pgx.Connect(ctx, dbURL)
|
|
33
|
-
if err != nil {
|
|
34
|
-
fmt.Fprintf(os.Stderr, "connect error: %v\n", err)
|
|
35
|
-
os.Exit(1)
|
|
36
|
-
}
|
|
37
|
-
defer conn.Close(ctx)
|
|
38
|
-
|
|
39
|
-
fmt.Println("--- go-structs integration test ---")
|
|
40
|
-
|
|
41
|
-
// 1. Query user into generated struct
|
|
42
|
-
var user models.Users
|
|
43
|
-
err = conn.QueryRow(ctx, "SELECT id, email, name, age, is_active FROM users WHERE id = 1").
|
|
44
|
-
Scan(&user.Id, &user.Email, &user.Name, &user.Age, &user.IsActive)
|
|
45
|
-
if err != nil {
|
|
46
|
-
fmt.Fprintf(os.Stderr, "query user error: %v\n", err)
|
|
47
|
-
os.Exit(1)
|
|
48
|
-
}
|
|
49
|
-
assert(user.Email == "test@example.com", "user.Email matches")
|
|
50
|
-
assert(user.Name != nil && *user.Name == "Test User", "user.Name matches")
|
|
51
|
-
assert(user.Age != nil && *user.Age == 30, "user.Age matches")
|
|
52
|
-
assert(user.IsActive == true, "user.IsActive matches")
|
|
53
|
-
|
|
54
|
-
// 2. Query post into generated struct
|
|
55
|
-
var post models.Posts
|
|
56
|
-
err = conn.QueryRow(ctx, "SELECT id, user_id, title, body, view_count FROM posts WHERE id = 1").
|
|
57
|
-
Scan(&post.Id, &post.UserId, &post.Title, &post.Body, &post.ViewCount)
|
|
58
|
-
if err != nil {
|
|
59
|
-
fmt.Fprintf(os.Stderr, "query post error: %v\n", err)
|
|
60
|
-
os.Exit(1)
|
|
61
|
-
}
|
|
62
|
-
assert(post.Title == "Hello World", "post.Title matches")
|
|
63
|
-
assert(post.UserId == 1, "post.UserId matches")
|
|
64
|
-
assert(post.ViewCount == 42, "post.ViewCount matches")
|
|
65
|
-
|
|
66
|
-
if failed > 0 {
|
|
67
|
-
fmt.Fprintf(os.Stderr, "\n%d assertion(s) failed\n", failed)
|
|
68
|
-
os.Exit(1)
|
|
69
|
-
}
|
|
70
|
-
fmt.Println("\nAll assertions passed!")
|
|
71
|
-
}
|
package/src/gorm/test/.gitignore
DELETED
package/src/gorm/test/Dockerfile
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
FROM golang:1.24-alpine
|
|
2
|
-
WORKDIR /app
|
|
3
|
-
# Set up Go module with models as a sub-package
|
|
4
|
-
RUN go mod init sqldoc-test
|
|
5
|
-
RUN mkdir -p models cmd/test
|
|
6
|
-
COPY models.go models/
|
|
7
|
-
COPY test.go cmd/test/main.go
|
|
8
|
-
ENV GOTOOLCHAIN=auto
|
|
9
|
-
RUN go get github.com/jackc/pgx/v5 gorm.io/gorm gorm.io/driver/postgres && go mod tidy
|
|
10
|
-
# Step 1: typecheck/compile all packages
|
|
11
|
-
RUN go build -o /usr/local/bin/test-runner ./cmd/test
|
|
12
|
-
# Step 2: run integration test against real DB
|
|
13
|
-
CMD ["test-runner"]
|
package/src/gorm/test/test.go
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"os"
|
|
6
|
-
|
|
7
|
-
"sqldoc-test/models"
|
|
8
|
-
|
|
9
|
-
"gorm.io/driver/postgres"
|
|
10
|
-
"gorm.io/gorm"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
var failed int
|
|
14
|
-
|
|
15
|
-
func assert(condition bool, msg string) {
|
|
16
|
-
if !condition {
|
|
17
|
-
fmt.Fprintf(os.Stderr, "FAIL: %s\n", msg)
|
|
18
|
-
failed++
|
|
19
|
-
} else {
|
|
20
|
-
fmt.Printf(" ok: %s\n", msg)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
func main() {
|
|
25
|
-
dbURL := os.Getenv("DATABASE_URL")
|
|
26
|
-
if dbURL == "" {
|
|
27
|
-
fmt.Fprintln(os.Stderr, "DATABASE_URL not set")
|
|
28
|
-
os.Exit(1)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
db, err := gorm.Open(postgres.Open(dbURL), &gorm.Config{})
|
|
32
|
-
if err != nil {
|
|
33
|
-
fmt.Fprintf(os.Stderr, "connect error: %v\n", err)
|
|
34
|
-
os.Exit(1)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
fmt.Println("--- gorm integration test ---")
|
|
38
|
-
|
|
39
|
-
// 1. Query user via GORM using generated model
|
|
40
|
-
var user models.Users
|
|
41
|
-
if err := db.First(&user, 1).Error; err != nil {
|
|
42
|
-
fmt.Fprintf(os.Stderr, "query user error: %v\n", err)
|
|
43
|
-
os.Exit(1)
|
|
44
|
-
}
|
|
45
|
-
assert(user.Email == "test@example.com", "user.Email matches")
|
|
46
|
-
assert(user.Name != nil && *user.Name == "Test User", "user.Name matches")
|
|
47
|
-
assert(user.Age != nil && *user.Age == 30, "user.Age matches")
|
|
48
|
-
assert(user.IsActive == true, "user.IsActive matches")
|
|
49
|
-
|
|
50
|
-
// 2. Query post via GORM using generated model
|
|
51
|
-
var post models.Posts
|
|
52
|
-
if err := db.First(&post, 1).Error; err != nil {
|
|
53
|
-
fmt.Fprintf(os.Stderr, "query post error: %v\n", err)
|
|
54
|
-
os.Exit(1)
|
|
55
|
-
}
|
|
56
|
-
assert(post.Title == "Hello World", "post.Title matches")
|
|
57
|
-
assert(post.UserId == 1, "post.UserId matches")
|
|
58
|
-
assert(post.ViewCount == 42, "post.ViewCount matches")
|
|
59
|
-
|
|
60
|
-
if failed > 0 {
|
|
61
|
-
fmt.Fprintf(os.Stderr, "\n%d assertion(s) failed\n", failed)
|
|
62
|
-
os.Exit(1)
|
|
63
|
-
}
|
|
64
|
-
fmt.Println("\nAll assertions passed!")
|
|
65
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
FROM eclipse-temurin:21-jdk-alpine
|
|
2
|
-
WORKDIR /app
|
|
3
|
-
RUN apk add --no-cache curl
|
|
4
|
-
# Download PostgreSQL JDBC driver
|
|
5
|
-
RUN mkdir -p /deps && \
|
|
6
|
-
curl -sL -o /deps/postgresql-42.7.4.jar https://repo1.maven.org/maven2/org/postgresql/postgresql/42.7.4/postgresql-42.7.4.jar
|
|
7
|
-
COPY . .
|
|
8
|
-
# Step 1: compile the generated records + test
|
|
9
|
-
RUN javac -cp "/deps/*:." *.java Test.java
|
|
10
|
-
# Step 2: run integration test against real DB
|
|
11
|
-
CMD ["java", "-cp", "/deps/*:.", "Test"]
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import java.sql.*;
|
|
2
|
-
import java.time.OffsetDateTime;
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Integration test for @sqldoc/templates/java-records
|
|
6
|
-
* Connects to real Postgres, verifies generated records work with actual data.
|
|
7
|
-
*/
|
|
8
|
-
public class Test {
|
|
9
|
-
static int failed = 0;
|
|
10
|
-
|
|
11
|
-
static void assertEq(Object actual, Object expected, String msg) {
|
|
12
|
-
if (!actual.equals(expected)) {
|
|
13
|
-
System.err.printf("FAIL: %s (got %s, expected %s)%n", msg, actual, expected);
|
|
14
|
-
failed++;
|
|
15
|
-
} else {
|
|
16
|
-
System.out.printf(" ok: %s%n", msg);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public static void main(String[] args) throws Exception {
|
|
21
|
-
String dbUrl = System.getenv("DATABASE_URL");
|
|
22
|
-
if (dbUrl == null || dbUrl.isEmpty()) {
|
|
23
|
-
System.err.println("DATABASE_URL not set");
|
|
24
|
-
System.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Convert postgres(ql):// to jdbc:postgresql://, extracting userinfo for JDBC
|
|
28
|
-
var uri = java.net.URI.create(dbUrl.replaceFirst("^postgres(ql)?://", "http://"));
|
|
29
|
-
var userInfo = uri.getUserInfo();
|
|
30
|
-
var jdbcUrl = "jdbc:postgresql://" + uri.getHost() + ":" + (uri.getPort() > 0 ? uri.getPort() : 5432) + uri.getPath();
|
|
31
|
-
var query = uri.getQuery();
|
|
32
|
-
if (userInfo != null) {
|
|
33
|
-
var parts = userInfo.split(":", 2);
|
|
34
|
-
var sep = query != null ? "&" : "?";
|
|
35
|
-
jdbcUrl += (query != null ? "?" + query : "") + sep + "user=" + parts[0] + "&password=" + (parts.length > 1 ? parts[1] : "");
|
|
36
|
-
} else if (query != null) {
|
|
37
|
-
jdbcUrl += "?" + query;
|
|
38
|
-
}
|
|
39
|
-
dbUrl = jdbcUrl;
|
|
40
|
-
|
|
41
|
-
System.out.println("--- java-records integration test ---");
|
|
42
|
-
|
|
43
|
-
try (Connection conn = DriverManager.getConnection(dbUrl)) {
|
|
44
|
-
// 1. Query user and construct generated record
|
|
45
|
-
try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = 1")) {
|
|
46
|
-
ResultSet rs = ps.executeQuery();
|
|
47
|
-
rs.next();
|
|
48
|
-
var user = new Users(
|
|
49
|
-
rs.getLong("id"),
|
|
50
|
-
rs.getString("email"),
|
|
51
|
-
rs.getString("name"),
|
|
52
|
-
rs.getObject("age") != null ? rs.getInt("age") : null,
|
|
53
|
-
rs.getBoolean("is_active"),
|
|
54
|
-
rs.getString("metadata"),
|
|
55
|
-
null, // address (composite)
|
|
56
|
-
rs.getObject("created_at", OffsetDateTime.class),
|
|
57
|
-
null, // tags (array)
|
|
58
|
-
rs.getBytes("avatar"),
|
|
59
|
-
rs.getBigDecimal("balance"),
|
|
60
|
-
rs.getObject("external_id") != null ? java.util.UUID.fromString(rs.getString("external_id")) : null
|
|
61
|
-
);
|
|
62
|
-
assertEq(user.email(), "test@example.com", "user.email() matches");
|
|
63
|
-
assertEq(user.name(), "Test User", "user.name() matches");
|
|
64
|
-
assertEq(user.age(), 30, "user.age() matches");
|
|
65
|
-
assertEq(user.isActive(), true, "user.isActive() matches");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// 2. Query post and construct generated record
|
|
69
|
-
try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM posts WHERE id = 1")) {
|
|
70
|
-
ResultSet rs = ps.executeQuery();
|
|
71
|
-
rs.next();
|
|
72
|
-
var post = new Posts(
|
|
73
|
-
rs.getLong("id"),
|
|
74
|
-
rs.getLong("user_id"),
|
|
75
|
-
rs.getString("title"),
|
|
76
|
-
rs.getString("body"),
|
|
77
|
-
rs.getObject("published_at") != null ? rs.getObject("published_at", OffsetDateTime.class) : null,
|
|
78
|
-
rs.getInt("view_count"),
|
|
79
|
-
rs.getObject("rating") != null ? rs.getDouble("rating") : null
|
|
80
|
-
);
|
|
81
|
-
assertEq(post.title(), "Hello World", "post.title() matches");
|
|
82
|
-
assertEq(post.userId(), 1L, "post.userId() matches");
|
|
83
|
-
assertEq(post.viewCount(), 42, "post.viewCount() matches");
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (failed > 0) {
|
|
88
|
-
System.err.printf("%n%d assertion(s) failed%n", failed);
|
|
89
|
-
System.exit(1);
|
|
90
|
-
}
|
|
91
|
-
System.out.println("\nAll assertions passed!");
|
|
92
|
-
}
|
|
93
|
-
}
|