postgresdk 0.6.6 → 0.6.8
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/dist/cli.js +223 -135
- package/dist/index.js +223 -135
- package/package.json +1 -1
package/dist/cli.js
CHANGED
@@ -1005,7 +1005,7 @@ function emitZod(table, opts) {
|
|
1005
1005
|
const Type = pascal(table.name);
|
1006
1006
|
const zFor = (pg) => {
|
1007
1007
|
if (pg === "uuid")
|
1008
|
-
return `z.string()
|
1008
|
+
return `z.string()`;
|
1009
1009
|
if (pg === "bool" || pg === "boolean")
|
1010
1010
|
return `z.boolean()`;
|
1011
1011
|
if (pg === "int2" || pg === "int4" || pg === "int8")
|
@@ -2370,8 +2370,8 @@ function emitTableTest(table, clientPath, framework = "vitest") {
|
|
2370
2370
|
const imports = getFrameworkImports(framework);
|
2371
2371
|
const hasForeignKeys = table.fks.length > 0;
|
2372
2372
|
const foreignKeySetup = hasForeignKeys ? generateForeignKeySetup(table, clientPath) : null;
|
2373
|
-
const sampleData =
|
2374
|
-
const updateData =
|
2373
|
+
const sampleData = generateSampleDataFromSchema(table, hasForeignKeys);
|
2374
|
+
const updateData = generateUpdateDataFromSchema(table);
|
2375
2375
|
return `${imports}
|
2376
2376
|
import { SDK } from '${clientPath}';
|
2377
2377
|
import type { Insert${Type}, Update${Type}, Select${Type} } from '${clientPath}/types/${tableName}';
|
@@ -2381,12 +2381,12 @@ ${foreignKeySetup?.imports || ""}
|
|
2381
2381
|
* Basic tests for ${tableName} table operations
|
2382
2382
|
*
|
2383
2383
|
* These tests demonstrate basic CRUD operations.
|
2384
|
-
* The test data is auto-generated
|
2384
|
+
* The test data is auto-generated based on your schema.
|
2385
2385
|
*
|
2386
|
-
* If tests fail
|
2387
|
-
* 1. Check
|
2388
|
-
* 2. Update the test data below to match your
|
2389
|
-
* 3. Consider adding
|
2386
|
+
* If tests fail:
|
2387
|
+
* 1. Check the error messages for missing required fields
|
2388
|
+
* 2. Update the test data below to match your business requirements
|
2389
|
+
* 3. Consider adding custom tests for business logic in separate files
|
2390
2390
|
*/
|
2391
2391
|
describe('${Type} SDK Operations', () => {
|
2392
2392
|
let sdk: SDK;
|
@@ -2465,13 +2465,6 @@ export default defineConfig({
|
|
2465
2465
|
testTimeout: 30000,
|
2466
2466
|
hookTimeout: 30000,
|
2467
2467
|
// The reporters are configured via CLI in the test script
|
2468
|
-
// Force color output in terminal
|
2469
|
-
pool: 'forks',
|
2470
|
-
poolOptions: {
|
2471
|
-
forks: {
|
2472
|
-
singleFork: true,
|
2473
|
-
}
|
2474
|
-
}
|
2475
2468
|
},
|
2476
2469
|
});
|
2477
2470
|
`;
|
@@ -2506,6 +2499,7 @@ version: '3.8'
|
|
2506
2499
|
services:
|
2507
2500
|
postgres:
|
2508
2501
|
image: postgres:17-alpine
|
2502
|
+
container_name: postgresdk-test-database
|
2509
2503
|
environment:
|
2510
2504
|
POSTGRES_USER: testuser
|
2511
2505
|
POSTGRES_PASSWORD: testpass
|
@@ -2544,7 +2538,39 @@ set -e
|
|
2544
2538
|
|
2545
2539
|
SCRIPT_DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd )"
|
2546
2540
|
|
2547
|
-
|
2541
|
+
# Cleanup function to ensure database is stopped
|
2542
|
+
cleanup() {
|
2543
|
+
echo ""
|
2544
|
+
echo "\uD83E\uDDF9 Cleaning up..."
|
2545
|
+
if [ ! -z "\${SERVER_PID}" ]; then
|
2546
|
+
echo " Stopping API server..."
|
2547
|
+
kill $SERVER_PID 2>/dev/null || true
|
2548
|
+
fi
|
2549
|
+
echo " Stopping test database..."
|
2550
|
+
docker-compose -f "$SCRIPT_DIR/docker-compose.yml" stop 2>/dev/null || true
|
2551
|
+
echo " Done!"
|
2552
|
+
}
|
2553
|
+
|
2554
|
+
# Set up cleanup trap
|
2555
|
+
trap cleanup EXIT INT TERM
|
2556
|
+
|
2557
|
+
# Check for existing PostgreSQL container or connection
|
2558
|
+
echo "\uD83D\uDD0D Checking for existing database connections..."
|
2559
|
+
if docker ps | grep -q "5432->5432"; then
|
2560
|
+
echo "⚠️ Found existing PostgreSQL container on port 5432"
|
2561
|
+
echo " Stopping existing container..."
|
2562
|
+
docker ps --filter "publish=5432" --format "{{.ID}}" | xargs -r docker stop
|
2563
|
+
sleep 2
|
2564
|
+
fi
|
2565
|
+
|
2566
|
+
# Clean up any existing test database container
|
2567
|
+
if docker ps -a | grep -q "postgresdk-test-database"; then
|
2568
|
+
echo "\uD83E\uDDF9 Cleaning up existing test database container..."
|
2569
|
+
docker-compose -f "$SCRIPT_DIR/docker-compose.yml" down -v
|
2570
|
+
sleep 2
|
2571
|
+
fi
|
2572
|
+
|
2573
|
+
echo "\uD83D\uDC33 Starting fresh test database..."
|
2548
2574
|
cd "$SCRIPT_DIR"
|
2549
2575
|
docker-compose up -d --wait
|
2550
2576
|
|
@@ -2552,10 +2578,6 @@ docker-compose up -d --wait
|
|
2552
2578
|
export TEST_DATABASE_URL="postgres://testuser:testpass@localhost:5432/testdb"
|
2553
2579
|
export TEST_API_URL="http://localhost:3000"
|
2554
2580
|
|
2555
|
-
# Force color output for better terminal experience
|
2556
|
-
export FORCE_COLOR=1
|
2557
|
-
export CI=""
|
2558
|
-
|
2559
2581
|
# Wait for database to be ready
|
2560
2582
|
echo "⏳ Waiting for database..."
|
2561
2583
|
sleep 3
|
@@ -2570,11 +2592,11 @@ echo "⚠️ TODO: Uncomment and customize the API server startup command below
|
|
2570
2592
|
echo ""
|
2571
2593
|
echo " # Example for Node.js/Bun:"
|
2572
2594
|
echo " # cd ../.. && npm run dev &"
|
2573
|
-
echo " # SERVER_PID
|
2595
|
+
echo " # SERVER_PID=\\$!"
|
2574
2596
|
echo ""
|
2575
2597
|
echo " # Example for custom server file:"
|
2576
2598
|
echo " # cd ../.. && node server.js &"
|
2577
|
-
echo " # SERVER_PID
|
2599
|
+
echo " # SERVER_PID=\\$!"
|
2578
2600
|
echo ""
|
2579
2601
|
echo " Please edit this script to start your API server."
|
2580
2602
|
echo ""
|
@@ -2592,12 +2614,6 @@ ${getTestCommand(framework, runCommand)}
|
|
2592
2614
|
|
2593
2615
|
TEST_EXIT_CODE=$?
|
2594
2616
|
|
2595
|
-
# Cleanup
|
2596
|
-
# if [ ! -z "\${SERVER_PID}" ]; then
|
2597
|
-
# echo "\uD83D\uDED1 Stopping API server..."
|
2598
|
-
# kill $SERVER_PID 2>/dev/null || true
|
2599
|
-
# fi
|
2600
|
-
|
2601
2617
|
if [ $TEST_EXIT_CODE -eq 0 ]; then
|
2602
2618
|
echo "✅ Tests completed successfully!"
|
2603
2619
|
else
|
@@ -2608,8 +2624,10 @@ echo ""
|
|
2608
2624
|
echo "\uD83D\uDCCA Test results saved to:"
|
2609
2625
|
echo " $TEST_RESULTS_DIR/"
|
2610
2626
|
echo ""
|
2611
|
-
echo "
|
2612
|
-
echo "
|
2627
|
+
echo "\uD83D\uDCA1 Tips:"
|
2628
|
+
echo " - Database will be stopped automatically on script exit"
|
2629
|
+
echo " - To manually stop the database: docker-compose -f $SCRIPT_DIR/docker-compose.yml down"
|
2630
|
+
echo " - To reset the database: docker-compose -f $SCRIPT_DIR/docker-compose.yml down -v"
|
2613
2631
|
|
2614
2632
|
exit $TEST_EXIT_CODE
|
2615
2633
|
`;
|
@@ -2629,7 +2647,7 @@ function generateForeignKeySetup(table, clientPath) {
|
|
2629
2647
|
variables.push(`let ${foreignTable}Id: string;`);
|
2630
2648
|
setupStatements.push(`
|
2631
2649
|
// Create parent ${foreignTable} record for foreign key reference
|
2632
|
-
const ${foreignTable}Data = ${
|
2650
|
+
const ${foreignTable}Data: Insert${ForeignType} = ${generateMinimalDataForTable(foreignTable)};
|
2633
2651
|
const created${ForeignType} = await sdk.${foreignTable}.create(${foreignTable}Data);
|
2634
2652
|
${foreignTable}Id = created${ForeignType}.id;`);
|
2635
2653
|
cleanupStatements.push(`
|
@@ -2652,29 +2670,40 @@ function generateForeignKeySetup(table, clientPath) {
|
|
2652
2670
|
cleanup: cleanupStatements.join("")
|
2653
2671
|
};
|
2654
2672
|
}
|
2655
|
-
function
|
2656
|
-
|
2657
|
-
|
2658
|
-
|
2659
|
-
|
2660
|
-
|
2661
|
-
|
2662
|
-
|
2663
|
-
|
2664
|
-
|
2665
|
-
|
2666
|
-
|
2673
|
+
function generateMinimalDataForTable(tableName) {
|
2674
|
+
if (tableName.includes("author")) {
|
2675
|
+
return `{ name: 'Test Author' }`;
|
2676
|
+
}
|
2677
|
+
if (tableName.includes("book")) {
|
2678
|
+
return `{ title: 'Test Book' }`;
|
2679
|
+
}
|
2680
|
+
if (tableName.includes("tag")) {
|
2681
|
+
return `{ name: 'Test Tag' }`;
|
2682
|
+
}
|
2683
|
+
if (tableName.includes("user")) {
|
2684
|
+
return `{ name: 'Test User', email: 'test@example.com' }`;
|
2685
|
+
}
|
2686
|
+
if (tableName.includes("category") || tableName.includes("categories")) {
|
2687
|
+
return `{ name: 'Test Category' }`;
|
2688
|
+
}
|
2689
|
+
if (tableName.includes("product")) {
|
2690
|
+
return `{ name: 'Test Product', price: 10.99 }`;
|
2691
|
+
}
|
2692
|
+
if (tableName.includes("order")) {
|
2693
|
+
return `{ total: 100.00, status: 'pending' }`;
|
2694
|
+
}
|
2695
|
+
return `{ name: 'Test ${pascal(tableName)}' }`;
|
2667
2696
|
}
|
2668
2697
|
function getTestCommand(framework, baseCommand) {
|
2669
2698
|
switch (framework) {
|
2670
2699
|
case "vitest":
|
2671
|
-
return
|
2700
|
+
return `${baseCommand} --reporter=default --reporter=json --outputFile="$TEST_RESULTS_DIR/results-\${TIMESTAMP}.json" "$@"`;
|
2672
2701
|
case "jest":
|
2673
|
-
return
|
2702
|
+
return `${baseCommand} --json --outputFile="$TEST_RESULTS_DIR/results-\${TIMESTAMP}.json" "$@"`;
|
2674
2703
|
case "bun":
|
2675
|
-
return `
|
2704
|
+
return `NO_COLOR=1 ${baseCommand} "$@" 2>&1 | tee "$TEST_RESULTS_DIR/results-\${TIMESTAMP}.txt"`;
|
2676
2705
|
default:
|
2677
|
-
return
|
2706
|
+
return `${baseCommand} "$@"`;
|
2678
2707
|
}
|
2679
2708
|
}
|
2680
2709
|
function getFrameworkImports(framework) {
|
@@ -2689,35 +2718,39 @@ function getFrameworkImports(framework) {
|
|
2689
2718
|
return "import { describe, it, expect, beforeAll, afterAll } from 'vitest';";
|
2690
2719
|
}
|
2691
2720
|
}
|
2692
|
-
function
|
2721
|
+
function generateSampleDataFromSchema(table, hasForeignKeys = false) {
|
2693
2722
|
const fields = [];
|
2694
2723
|
const foreignKeyColumns = new Map;
|
2695
2724
|
if (hasForeignKeys) {
|
2696
2725
|
for (const fk of table.fks) {
|
2697
|
-
|
2698
|
-
|
2726
|
+
for (let i = 0;i < fk.from.length; i++) {
|
2727
|
+
const fromCol = fk.from[i];
|
2728
|
+
if (fromCol) {
|
2729
|
+
foreignKeyColumns.set(fromCol, fk.toTable);
|
2730
|
+
}
|
2699
2731
|
}
|
2700
2732
|
}
|
2701
2733
|
}
|
2702
2734
|
for (const col of table.columns) {
|
2703
|
-
|
2704
|
-
|
2705
|
-
}
|
2706
|
-
if ((col.name === "created_at" || col.name === "updated_at") && col.hasDefault) {
|
2735
|
+
const isAutoGenerated = col.hasDefault && ["id", "created_at", "updated_at", "created", "updated", "modified_at"].includes(col.name.toLowerCase());
|
2736
|
+
if (isAutoGenerated) {
|
2707
2737
|
continue;
|
2708
2738
|
}
|
2709
|
-
if (col.name === "deleted_at") {
|
2739
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2740
|
+
if (col.nullable) {
|
2741
|
+
fields.push(` ${col.name}: null`);
|
2742
|
+
} else {
|
2743
|
+
const value = generateValueForColumn(col);
|
2744
|
+
fields.push(` ${col.name}: ${value}`);
|
2745
|
+
}
|
2710
2746
|
continue;
|
2711
2747
|
}
|
2712
|
-
|
2713
|
-
|
2714
|
-
|
2715
|
-
|
2716
|
-
|
2717
|
-
|
2718
|
-
if (isImportant) {
|
2719
|
-
const foreignTable = foreignKeyColumns.get(col.name);
|
2720
|
-
const value = foreignTable ? `${foreignTable}Id` : getSampleValue(col.pgType, col.name);
|
2748
|
+
const foreignTable = foreignKeyColumns.get(col.name);
|
2749
|
+
if (!col.nullable || foreignTable || col.hasDefault && !isAutoGenerated || shouldIncludeNullableColumn(col)) {
|
2750
|
+
if (foreignTable) {
|
2751
|
+
fields.push(` ${col.name}: ${foreignTable}Id`);
|
2752
|
+
} else {
|
2753
|
+
const value = generateValueForColumn(col);
|
2721
2754
|
fields.push(` ${col.name}: ${value}`);
|
2722
2755
|
}
|
2723
2756
|
}
|
@@ -2727,14 +2760,23 @@ ${fields.join(`,
|
|
2727
2760
|
`)}
|
2728
2761
|
}` : "{}";
|
2729
2762
|
}
|
2730
|
-
function
|
2763
|
+
function generateUpdateDataFromSchema(table) {
|
2731
2764
|
const fields = [];
|
2732
2765
|
for (const col of table.columns) {
|
2733
|
-
if (
|
2766
|
+
if (table.pk.includes(col.name) || col.hasDefault) {
|
2767
|
+
const autoGenerated = ["id", "created_at", "updated_at", "created", "updated", "modified_at"];
|
2768
|
+
if (autoGenerated.includes(col.name.toLowerCase())) {
|
2769
|
+
continue;
|
2770
|
+
}
|
2771
|
+
}
|
2772
|
+
if (col.name.endsWith("_id")) {
|
2734
2773
|
continue;
|
2735
2774
|
}
|
2736
|
-
if (
|
2737
|
-
|
2775
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2776
|
+
continue;
|
2777
|
+
}
|
2778
|
+
if (!col.nullable || shouldIncludeNullableColumn(col)) {
|
2779
|
+
const value = generateValueForColumn(col, true);
|
2738
2780
|
fields.push(` ${col.name}: ${value}`);
|
2739
2781
|
break;
|
2740
2782
|
}
|
@@ -2744,104 +2786,150 @@ ${fields.join(`,
|
|
2744
2786
|
`)}
|
2745
2787
|
}` : "{}";
|
2746
2788
|
}
|
2747
|
-
function
|
2748
|
-
const
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
2752
|
-
|
2753
|
-
|
2754
|
-
|
2755
|
-
|
2756
|
-
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
2765
|
-
|
2766
|
-
|
2767
|
-
|
2768
|
-
|
2769
|
-
|
2770
|
-
|
2771
|
-
return `'Test Subject${isUpdate ? " Updated" : ""}'`;
|
2772
|
-
}
|
2773
|
-
if (name.includes("name") || name.includes("title")) {
|
2774
|
-
return `'Test ${pascal(name)}'${suffix}`;
|
2775
|
-
}
|
2776
|
-
if (name.includes("description") || name.includes("bio") || name.includes("content")) {
|
2777
|
-
return `'Test description'${suffix}`;
|
2778
|
-
}
|
2779
|
-
if (name.includes("preferences") || name.includes("settings")) {
|
2780
|
-
return `'Test preferences'${suffix}`;
|
2781
|
-
}
|
2782
|
-
if (name.includes("restrictions") || name.includes("dietary")) {
|
2783
|
-
return `['vegetarian']`;
|
2784
|
-
}
|
2785
|
-
if (name.includes("location") || name.includes("clinic")) {
|
2786
|
-
return `'Test Location'${suffix}`;
|
2787
|
-
}
|
2788
|
-
if (name.includes("specialty")) {
|
2789
|
-
return `'General'`;
|
2790
|
-
}
|
2791
|
-
if (name.includes("tier")) {
|
2792
|
-
return `'Standard'`;
|
2793
|
-
}
|
2794
|
-
if (name.includes("emergency")) {
|
2795
|
-
return `'Emergency Contact ${isUpdate ? "Updated" : "Name"}'`;
|
2796
|
-
}
|
2797
|
-
if (name === "date_of_birth" || name.includes("birth")) {
|
2798
|
-
return `new Date('1990-01-01')`;
|
2799
|
-
}
|
2800
|
-
if (name.includes("accommodations")) {
|
2801
|
-
return `'No special accommodations'`;
|
2802
|
-
}
|
2803
|
-
if (name.includes("flight")) {
|
2804
|
-
return `'Economy'`;
|
2805
|
-
}
|
2789
|
+
function shouldIncludeNullableColumn(col) {
|
2790
|
+
const importantPatterns = [
|
2791
|
+
"_id",
|
2792
|
+
"_by",
|
2793
|
+
"email",
|
2794
|
+
"name",
|
2795
|
+
"title",
|
2796
|
+
"description",
|
2797
|
+
"phone",
|
2798
|
+
"address",
|
2799
|
+
"status",
|
2800
|
+
"type",
|
2801
|
+
"category",
|
2802
|
+
"price",
|
2803
|
+
"amount",
|
2804
|
+
"quantity",
|
2805
|
+
"url",
|
2806
|
+
"slug"
|
2807
|
+
];
|
2808
|
+
const name = col.name.toLowerCase();
|
2809
|
+
return importantPatterns.some((pattern) => name.includes(pattern));
|
2810
|
+
}
|
2811
|
+
function generateValueForColumn(col, isUpdate = false) {
|
2812
|
+
const type = col.pgType.toLowerCase();
|
2806
2813
|
switch (type) {
|
2807
2814
|
case "text":
|
2808
2815
|
case "varchar":
|
2809
2816
|
case "char":
|
2810
|
-
|
2817
|
+
case "character varying":
|
2818
|
+
case "bpchar":
|
2819
|
+
case "name":
|
2820
|
+
return `'str_${Math.random().toString(36).substring(7)}'`;
|
2811
2821
|
case "int":
|
2822
|
+
case "int2":
|
2823
|
+
case "int4":
|
2824
|
+
case "int8":
|
2812
2825
|
case "integer":
|
2813
2826
|
case "smallint":
|
2814
2827
|
case "bigint":
|
2828
|
+
case "serial":
|
2829
|
+
case "bigserial":
|
2815
2830
|
return isUpdate ? "42" : "1";
|
2816
2831
|
case "decimal":
|
2817
2832
|
case "numeric":
|
2818
2833
|
case "real":
|
2819
2834
|
case "double precision":
|
2820
2835
|
case "float":
|
2836
|
+
case "float4":
|
2837
|
+
case "float8":
|
2838
|
+
case "money":
|
2821
2839
|
return isUpdate ? "99.99" : "10.50";
|
2822
2840
|
case "boolean":
|
2823
2841
|
case "bool":
|
2824
2842
|
return isUpdate ? "false" : "true";
|
2825
2843
|
case "date":
|
2826
|
-
return `'2024-01-01'`;
|
2827
2844
|
case "timestamp":
|
2828
2845
|
case "timestamptz":
|
2846
|
+
case "timestamp without time zone":
|
2847
|
+
case "timestamp with time zone":
|
2848
|
+
case "time":
|
2849
|
+
case "timetz":
|
2850
|
+
case "time without time zone":
|
2851
|
+
case "time with time zone":
|
2829
2852
|
return `new Date()`;
|
2853
|
+
case "interval":
|
2854
|
+
return `'1 day'`;
|
2830
2855
|
case "json":
|
2831
2856
|
case "jsonb":
|
2832
|
-
return `{
|
2857
|
+
return `{}`;
|
2833
2858
|
case "uuid":
|
2834
|
-
return `'${
|
2835
|
-
case "
|
2836
|
-
|
2837
|
-
|
2859
|
+
return `'${generateUUID()}'`;
|
2860
|
+
case "inet":
|
2861
|
+
return `'192.168.1.1'`;
|
2862
|
+
case "cidr":
|
2863
|
+
return `'192.168.1.0/24'`;
|
2864
|
+
case "macaddr":
|
2865
|
+
case "macaddr8":
|
2866
|
+
return `'08:00:2b:01:02:03'`;
|
2867
|
+
case "point":
|
2868
|
+
return `'(1,2)'`;
|
2869
|
+
case "line":
|
2870
|
+
return `'{1,2,3}'`;
|
2871
|
+
case "lseg":
|
2872
|
+
return `'[(0,0),(1,1)]'`;
|
2873
|
+
case "box":
|
2874
|
+
return `'((0,0),(1,1))'`;
|
2875
|
+
case "path":
|
2876
|
+
return `'[(0,0),(1,1),(2,0)]'`;
|
2877
|
+
case "polygon":
|
2878
|
+
return `'((0,0),(1,1),(1,0))'`;
|
2879
|
+
case "circle":
|
2880
|
+
return `'<(0,0),1>'`;
|
2881
|
+
case "bit":
|
2882
|
+
case "bit varying":
|
2883
|
+
case "varbit":
|
2884
|
+
return `'101'`;
|
2885
|
+
case "bytea":
|
2886
|
+
return `'\\\\x0102'`;
|
2887
|
+
case "xml":
|
2888
|
+
return `'<root/>'`;
|
2889
|
+
case "tsvector":
|
2890
|
+
return `'a fat cat'`;
|
2891
|
+
case "tsquery":
|
2892
|
+
return `'fat & cat'`;
|
2893
|
+
case "oid":
|
2894
|
+
case "regproc":
|
2895
|
+
case "regprocedure":
|
2896
|
+
case "regoper":
|
2897
|
+
case "regoperator":
|
2898
|
+
case "regclass":
|
2899
|
+
case "regtype":
|
2900
|
+
case "regconfig":
|
2901
|
+
case "regdictionary":
|
2902
|
+
return "1";
|
2838
2903
|
default:
|
2839
|
-
|
2904
|
+
if (type.endsWith("[]") || type.startsWith("_")) {
|
2905
|
+
return `[]`;
|
2906
|
+
}
|
2907
|
+
return `'value1'`;
|
2840
2908
|
}
|
2841
2909
|
}
|
2910
|
+
function generateUUID() {
|
2911
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
2912
|
+
const r = Math.random() * 16 | 0;
|
2913
|
+
const v = c === "x" ? r : r & 3 | 8;
|
2914
|
+
return v.toString(16);
|
2915
|
+
});
|
2916
|
+
}
|
2842
2917
|
function generateTestCases(table, sampleData, updateData, hasForeignKeys = false) {
|
2843
2918
|
const Type = pascal(table.name);
|
2844
2919
|
const hasData = sampleData !== "{}";
|
2920
|
+
const isJunctionTable = table.pk.length > 1 && table.columns.every((col) => table.pk.includes(col.name) || col.name.endsWith("_id"));
|
2921
|
+
if (isJunctionTable) {
|
2922
|
+
return `it('should create a ${table.name} relationship', async () => {
|
2923
|
+
// This is a junction table for M:N relationships
|
2924
|
+
// Test data depends on parent records created in other tests
|
2925
|
+
expect(true).toBe(true);
|
2926
|
+
});
|
2927
|
+
|
2928
|
+
it('should list ${table.name} relationships', async () => {
|
2929
|
+
const list = await sdk.${table.name}.list({ limit: 10 });
|
2930
|
+
expect(Array.isArray(list)).toBe(true);
|
2931
|
+
});`;
|
2932
|
+
}
|
2845
2933
|
return `it('should create a ${table.name}', async () => {
|
2846
2934
|
const data: Insert${Type} = ${sampleData};
|
2847
2935
|
${hasData ? `
|
package/dist/index.js
CHANGED
@@ -735,7 +735,7 @@ function emitZod(table, opts) {
|
|
735
735
|
const Type = pascal(table.name);
|
736
736
|
const zFor = (pg) => {
|
737
737
|
if (pg === "uuid")
|
738
|
-
return `z.string()
|
738
|
+
return `z.string()`;
|
739
739
|
if (pg === "bool" || pg === "boolean")
|
740
740
|
return `z.boolean()`;
|
741
741
|
if (pg === "int2" || pg === "int4" || pg === "int8")
|
@@ -2100,8 +2100,8 @@ function emitTableTest(table, clientPath, framework = "vitest") {
|
|
2100
2100
|
const imports = getFrameworkImports(framework);
|
2101
2101
|
const hasForeignKeys = table.fks.length > 0;
|
2102
2102
|
const foreignKeySetup = hasForeignKeys ? generateForeignKeySetup(table, clientPath) : null;
|
2103
|
-
const sampleData =
|
2104
|
-
const updateData =
|
2103
|
+
const sampleData = generateSampleDataFromSchema(table, hasForeignKeys);
|
2104
|
+
const updateData = generateUpdateDataFromSchema(table);
|
2105
2105
|
return `${imports}
|
2106
2106
|
import { SDK } from '${clientPath}';
|
2107
2107
|
import type { Insert${Type}, Update${Type}, Select${Type} } from '${clientPath}/types/${tableName}';
|
@@ -2111,12 +2111,12 @@ ${foreignKeySetup?.imports || ""}
|
|
2111
2111
|
* Basic tests for ${tableName} table operations
|
2112
2112
|
*
|
2113
2113
|
* These tests demonstrate basic CRUD operations.
|
2114
|
-
* The test data is auto-generated
|
2114
|
+
* The test data is auto-generated based on your schema.
|
2115
2115
|
*
|
2116
|
-
* If tests fail
|
2117
|
-
* 1. Check
|
2118
|
-
* 2. Update the test data below to match your
|
2119
|
-
* 3. Consider adding
|
2116
|
+
* If tests fail:
|
2117
|
+
* 1. Check the error messages for missing required fields
|
2118
|
+
* 2. Update the test data below to match your business requirements
|
2119
|
+
* 3. Consider adding custom tests for business logic in separate files
|
2120
2120
|
*/
|
2121
2121
|
describe('${Type} SDK Operations', () => {
|
2122
2122
|
let sdk: SDK;
|
@@ -2195,13 +2195,6 @@ export default defineConfig({
|
|
2195
2195
|
testTimeout: 30000,
|
2196
2196
|
hookTimeout: 30000,
|
2197
2197
|
// The reporters are configured via CLI in the test script
|
2198
|
-
// Force color output in terminal
|
2199
|
-
pool: 'forks',
|
2200
|
-
poolOptions: {
|
2201
|
-
forks: {
|
2202
|
-
singleFork: true,
|
2203
|
-
}
|
2204
|
-
}
|
2205
2198
|
},
|
2206
2199
|
});
|
2207
2200
|
`;
|
@@ -2236,6 +2229,7 @@ version: '3.8'
|
|
2236
2229
|
services:
|
2237
2230
|
postgres:
|
2238
2231
|
image: postgres:17-alpine
|
2232
|
+
container_name: postgresdk-test-database
|
2239
2233
|
environment:
|
2240
2234
|
POSTGRES_USER: testuser
|
2241
2235
|
POSTGRES_PASSWORD: testpass
|
@@ -2274,7 +2268,39 @@ set -e
|
|
2274
2268
|
|
2275
2269
|
SCRIPT_DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd )"
|
2276
2270
|
|
2277
|
-
|
2271
|
+
# Cleanup function to ensure database is stopped
|
2272
|
+
cleanup() {
|
2273
|
+
echo ""
|
2274
|
+
echo "\uD83E\uDDF9 Cleaning up..."
|
2275
|
+
if [ ! -z "\${SERVER_PID}" ]; then
|
2276
|
+
echo " Stopping API server..."
|
2277
|
+
kill $SERVER_PID 2>/dev/null || true
|
2278
|
+
fi
|
2279
|
+
echo " Stopping test database..."
|
2280
|
+
docker-compose -f "$SCRIPT_DIR/docker-compose.yml" stop 2>/dev/null || true
|
2281
|
+
echo " Done!"
|
2282
|
+
}
|
2283
|
+
|
2284
|
+
# Set up cleanup trap
|
2285
|
+
trap cleanup EXIT INT TERM
|
2286
|
+
|
2287
|
+
# Check for existing PostgreSQL container or connection
|
2288
|
+
echo "\uD83D\uDD0D Checking for existing database connections..."
|
2289
|
+
if docker ps | grep -q "5432->5432"; then
|
2290
|
+
echo "⚠️ Found existing PostgreSQL container on port 5432"
|
2291
|
+
echo " Stopping existing container..."
|
2292
|
+
docker ps --filter "publish=5432" --format "{{.ID}}" | xargs -r docker stop
|
2293
|
+
sleep 2
|
2294
|
+
fi
|
2295
|
+
|
2296
|
+
# Clean up any existing test database container
|
2297
|
+
if docker ps -a | grep -q "postgresdk-test-database"; then
|
2298
|
+
echo "\uD83E\uDDF9 Cleaning up existing test database container..."
|
2299
|
+
docker-compose -f "$SCRIPT_DIR/docker-compose.yml" down -v
|
2300
|
+
sleep 2
|
2301
|
+
fi
|
2302
|
+
|
2303
|
+
echo "\uD83D\uDC33 Starting fresh test database..."
|
2278
2304
|
cd "$SCRIPT_DIR"
|
2279
2305
|
docker-compose up -d --wait
|
2280
2306
|
|
@@ -2282,10 +2308,6 @@ docker-compose up -d --wait
|
|
2282
2308
|
export TEST_DATABASE_URL="postgres://testuser:testpass@localhost:5432/testdb"
|
2283
2309
|
export TEST_API_URL="http://localhost:3000"
|
2284
2310
|
|
2285
|
-
# Force color output for better terminal experience
|
2286
|
-
export FORCE_COLOR=1
|
2287
|
-
export CI=""
|
2288
|
-
|
2289
2311
|
# Wait for database to be ready
|
2290
2312
|
echo "⏳ Waiting for database..."
|
2291
2313
|
sleep 3
|
@@ -2300,11 +2322,11 @@ echo "⚠️ TODO: Uncomment and customize the API server startup command below
|
|
2300
2322
|
echo ""
|
2301
2323
|
echo " # Example for Node.js/Bun:"
|
2302
2324
|
echo " # cd ../.. && npm run dev &"
|
2303
|
-
echo " # SERVER_PID
|
2325
|
+
echo " # SERVER_PID=\\$!"
|
2304
2326
|
echo ""
|
2305
2327
|
echo " # Example for custom server file:"
|
2306
2328
|
echo " # cd ../.. && node server.js &"
|
2307
|
-
echo " # SERVER_PID
|
2329
|
+
echo " # SERVER_PID=\\$!"
|
2308
2330
|
echo ""
|
2309
2331
|
echo " Please edit this script to start your API server."
|
2310
2332
|
echo ""
|
@@ -2322,12 +2344,6 @@ ${getTestCommand(framework, runCommand)}
|
|
2322
2344
|
|
2323
2345
|
TEST_EXIT_CODE=$?
|
2324
2346
|
|
2325
|
-
# Cleanup
|
2326
|
-
# if [ ! -z "\${SERVER_PID}" ]; then
|
2327
|
-
# echo "\uD83D\uDED1 Stopping API server..."
|
2328
|
-
# kill $SERVER_PID 2>/dev/null || true
|
2329
|
-
# fi
|
2330
|
-
|
2331
2347
|
if [ $TEST_EXIT_CODE -eq 0 ]; then
|
2332
2348
|
echo "✅ Tests completed successfully!"
|
2333
2349
|
else
|
@@ -2338,8 +2354,10 @@ echo ""
|
|
2338
2354
|
echo "\uD83D\uDCCA Test results saved to:"
|
2339
2355
|
echo " $TEST_RESULTS_DIR/"
|
2340
2356
|
echo ""
|
2341
|
-
echo "
|
2342
|
-
echo "
|
2357
|
+
echo "\uD83D\uDCA1 Tips:"
|
2358
|
+
echo " - Database will be stopped automatically on script exit"
|
2359
|
+
echo " - To manually stop the database: docker-compose -f $SCRIPT_DIR/docker-compose.yml down"
|
2360
|
+
echo " - To reset the database: docker-compose -f $SCRIPT_DIR/docker-compose.yml down -v"
|
2343
2361
|
|
2344
2362
|
exit $TEST_EXIT_CODE
|
2345
2363
|
`;
|
@@ -2359,7 +2377,7 @@ function generateForeignKeySetup(table, clientPath) {
|
|
2359
2377
|
variables.push(`let ${foreignTable}Id: string;`);
|
2360
2378
|
setupStatements.push(`
|
2361
2379
|
// Create parent ${foreignTable} record for foreign key reference
|
2362
|
-
const ${foreignTable}Data = ${
|
2380
|
+
const ${foreignTable}Data: Insert${ForeignType} = ${generateMinimalDataForTable(foreignTable)};
|
2363
2381
|
const created${ForeignType} = await sdk.${foreignTable}.create(${foreignTable}Data);
|
2364
2382
|
${foreignTable}Id = created${ForeignType}.id;`);
|
2365
2383
|
cleanupStatements.push(`
|
@@ -2382,29 +2400,40 @@ function generateForeignKeySetup(table, clientPath) {
|
|
2382
2400
|
cleanup: cleanupStatements.join("")
|
2383
2401
|
};
|
2384
2402
|
}
|
2385
|
-
function
|
2386
|
-
|
2387
|
-
|
2388
|
-
|
2389
|
-
|
2390
|
-
|
2391
|
-
|
2392
|
-
|
2393
|
-
|
2394
|
-
|
2395
|
-
|
2396
|
-
|
2403
|
+
function generateMinimalDataForTable(tableName) {
|
2404
|
+
if (tableName.includes("author")) {
|
2405
|
+
return `{ name: 'Test Author' }`;
|
2406
|
+
}
|
2407
|
+
if (tableName.includes("book")) {
|
2408
|
+
return `{ title: 'Test Book' }`;
|
2409
|
+
}
|
2410
|
+
if (tableName.includes("tag")) {
|
2411
|
+
return `{ name: 'Test Tag' }`;
|
2412
|
+
}
|
2413
|
+
if (tableName.includes("user")) {
|
2414
|
+
return `{ name: 'Test User', email: 'test@example.com' }`;
|
2415
|
+
}
|
2416
|
+
if (tableName.includes("category") || tableName.includes("categories")) {
|
2417
|
+
return `{ name: 'Test Category' }`;
|
2418
|
+
}
|
2419
|
+
if (tableName.includes("product")) {
|
2420
|
+
return `{ name: 'Test Product', price: 10.99 }`;
|
2421
|
+
}
|
2422
|
+
if (tableName.includes("order")) {
|
2423
|
+
return `{ total: 100.00, status: 'pending' }`;
|
2424
|
+
}
|
2425
|
+
return `{ name: 'Test ${pascal(tableName)}' }`;
|
2397
2426
|
}
|
2398
2427
|
function getTestCommand(framework, baseCommand) {
|
2399
2428
|
switch (framework) {
|
2400
2429
|
case "vitest":
|
2401
|
-
return
|
2430
|
+
return `${baseCommand} --reporter=default --reporter=json --outputFile="$TEST_RESULTS_DIR/results-\${TIMESTAMP}.json" "$@"`;
|
2402
2431
|
case "jest":
|
2403
|
-
return
|
2432
|
+
return `${baseCommand} --json --outputFile="$TEST_RESULTS_DIR/results-\${TIMESTAMP}.json" "$@"`;
|
2404
2433
|
case "bun":
|
2405
|
-
return `
|
2434
|
+
return `NO_COLOR=1 ${baseCommand} "$@" 2>&1 | tee "$TEST_RESULTS_DIR/results-\${TIMESTAMP}.txt"`;
|
2406
2435
|
default:
|
2407
|
-
return
|
2436
|
+
return `${baseCommand} "$@"`;
|
2408
2437
|
}
|
2409
2438
|
}
|
2410
2439
|
function getFrameworkImports(framework) {
|
@@ -2419,35 +2448,39 @@ function getFrameworkImports(framework) {
|
|
2419
2448
|
return "import { describe, it, expect, beforeAll, afterAll } from 'vitest';";
|
2420
2449
|
}
|
2421
2450
|
}
|
2422
|
-
function
|
2451
|
+
function generateSampleDataFromSchema(table, hasForeignKeys = false) {
|
2423
2452
|
const fields = [];
|
2424
2453
|
const foreignKeyColumns = new Map;
|
2425
2454
|
if (hasForeignKeys) {
|
2426
2455
|
for (const fk of table.fks) {
|
2427
|
-
|
2428
|
-
|
2456
|
+
for (let i = 0;i < fk.from.length; i++) {
|
2457
|
+
const fromCol = fk.from[i];
|
2458
|
+
if (fromCol) {
|
2459
|
+
foreignKeyColumns.set(fromCol, fk.toTable);
|
2460
|
+
}
|
2429
2461
|
}
|
2430
2462
|
}
|
2431
2463
|
}
|
2432
2464
|
for (const col of table.columns) {
|
2433
|
-
|
2434
|
-
|
2435
|
-
}
|
2436
|
-
if ((col.name === "created_at" || col.name === "updated_at") && col.hasDefault) {
|
2465
|
+
const isAutoGenerated = col.hasDefault && ["id", "created_at", "updated_at", "created", "updated", "modified_at"].includes(col.name.toLowerCase());
|
2466
|
+
if (isAutoGenerated) {
|
2437
2467
|
continue;
|
2438
2468
|
}
|
2439
|
-
if (col.name === "deleted_at") {
|
2469
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2470
|
+
if (col.nullable) {
|
2471
|
+
fields.push(` ${col.name}: null`);
|
2472
|
+
} else {
|
2473
|
+
const value = generateValueForColumn(col);
|
2474
|
+
fields.push(` ${col.name}: ${value}`);
|
2475
|
+
}
|
2440
2476
|
continue;
|
2441
2477
|
}
|
2442
|
-
|
2443
|
-
|
2444
|
-
|
2445
|
-
|
2446
|
-
|
2447
|
-
|
2448
|
-
if (isImportant) {
|
2449
|
-
const foreignTable = foreignKeyColumns.get(col.name);
|
2450
|
-
const value = foreignTable ? `${foreignTable}Id` : getSampleValue(col.pgType, col.name);
|
2478
|
+
const foreignTable = foreignKeyColumns.get(col.name);
|
2479
|
+
if (!col.nullable || foreignTable || col.hasDefault && !isAutoGenerated || shouldIncludeNullableColumn(col)) {
|
2480
|
+
if (foreignTable) {
|
2481
|
+
fields.push(` ${col.name}: ${foreignTable}Id`);
|
2482
|
+
} else {
|
2483
|
+
const value = generateValueForColumn(col);
|
2451
2484
|
fields.push(` ${col.name}: ${value}`);
|
2452
2485
|
}
|
2453
2486
|
}
|
@@ -2457,14 +2490,23 @@ ${fields.join(`,
|
|
2457
2490
|
`)}
|
2458
2491
|
}` : "{}";
|
2459
2492
|
}
|
2460
|
-
function
|
2493
|
+
function generateUpdateDataFromSchema(table) {
|
2461
2494
|
const fields = [];
|
2462
2495
|
for (const col of table.columns) {
|
2463
|
-
if (
|
2496
|
+
if (table.pk.includes(col.name) || col.hasDefault) {
|
2497
|
+
const autoGenerated = ["id", "created_at", "updated_at", "created", "updated", "modified_at"];
|
2498
|
+
if (autoGenerated.includes(col.name.toLowerCase())) {
|
2499
|
+
continue;
|
2500
|
+
}
|
2501
|
+
}
|
2502
|
+
if (col.name.endsWith("_id")) {
|
2464
2503
|
continue;
|
2465
2504
|
}
|
2466
|
-
if (
|
2467
|
-
|
2505
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2506
|
+
continue;
|
2507
|
+
}
|
2508
|
+
if (!col.nullable || shouldIncludeNullableColumn(col)) {
|
2509
|
+
const value = generateValueForColumn(col, true);
|
2468
2510
|
fields.push(` ${col.name}: ${value}`);
|
2469
2511
|
break;
|
2470
2512
|
}
|
@@ -2474,104 +2516,150 @@ ${fields.join(`,
|
|
2474
2516
|
`)}
|
2475
2517
|
}` : "{}";
|
2476
2518
|
}
|
2477
|
-
function
|
2478
|
-
const
|
2479
|
-
|
2480
|
-
|
2481
|
-
|
2482
|
-
|
2483
|
-
|
2484
|
-
|
2485
|
-
|
2486
|
-
|
2487
|
-
|
2488
|
-
|
2489
|
-
|
2490
|
-
|
2491
|
-
|
2492
|
-
|
2493
|
-
|
2494
|
-
|
2495
|
-
|
2496
|
-
|
2497
|
-
|
2498
|
-
|
2499
|
-
|
2500
|
-
|
2501
|
-
return `'Test Subject${isUpdate ? " Updated" : ""}'`;
|
2502
|
-
}
|
2503
|
-
if (name.includes("name") || name.includes("title")) {
|
2504
|
-
return `'Test ${pascal(name)}'${suffix}`;
|
2505
|
-
}
|
2506
|
-
if (name.includes("description") || name.includes("bio") || name.includes("content")) {
|
2507
|
-
return `'Test description'${suffix}`;
|
2508
|
-
}
|
2509
|
-
if (name.includes("preferences") || name.includes("settings")) {
|
2510
|
-
return `'Test preferences'${suffix}`;
|
2511
|
-
}
|
2512
|
-
if (name.includes("restrictions") || name.includes("dietary")) {
|
2513
|
-
return `['vegetarian']`;
|
2514
|
-
}
|
2515
|
-
if (name.includes("location") || name.includes("clinic")) {
|
2516
|
-
return `'Test Location'${suffix}`;
|
2517
|
-
}
|
2518
|
-
if (name.includes("specialty")) {
|
2519
|
-
return `'General'`;
|
2520
|
-
}
|
2521
|
-
if (name.includes("tier")) {
|
2522
|
-
return `'Standard'`;
|
2523
|
-
}
|
2524
|
-
if (name.includes("emergency")) {
|
2525
|
-
return `'Emergency Contact ${isUpdate ? "Updated" : "Name"}'`;
|
2526
|
-
}
|
2527
|
-
if (name === "date_of_birth" || name.includes("birth")) {
|
2528
|
-
return `new Date('1990-01-01')`;
|
2529
|
-
}
|
2530
|
-
if (name.includes("accommodations")) {
|
2531
|
-
return `'No special accommodations'`;
|
2532
|
-
}
|
2533
|
-
if (name.includes("flight")) {
|
2534
|
-
return `'Economy'`;
|
2535
|
-
}
|
2519
|
+
function shouldIncludeNullableColumn(col) {
|
2520
|
+
const importantPatterns = [
|
2521
|
+
"_id",
|
2522
|
+
"_by",
|
2523
|
+
"email",
|
2524
|
+
"name",
|
2525
|
+
"title",
|
2526
|
+
"description",
|
2527
|
+
"phone",
|
2528
|
+
"address",
|
2529
|
+
"status",
|
2530
|
+
"type",
|
2531
|
+
"category",
|
2532
|
+
"price",
|
2533
|
+
"amount",
|
2534
|
+
"quantity",
|
2535
|
+
"url",
|
2536
|
+
"slug"
|
2537
|
+
];
|
2538
|
+
const name = col.name.toLowerCase();
|
2539
|
+
return importantPatterns.some((pattern) => name.includes(pattern));
|
2540
|
+
}
|
2541
|
+
function generateValueForColumn(col, isUpdate = false) {
|
2542
|
+
const type = col.pgType.toLowerCase();
|
2536
2543
|
switch (type) {
|
2537
2544
|
case "text":
|
2538
2545
|
case "varchar":
|
2539
2546
|
case "char":
|
2540
|
-
|
2547
|
+
case "character varying":
|
2548
|
+
case "bpchar":
|
2549
|
+
case "name":
|
2550
|
+
return `'str_${Math.random().toString(36).substring(7)}'`;
|
2541
2551
|
case "int":
|
2552
|
+
case "int2":
|
2553
|
+
case "int4":
|
2554
|
+
case "int8":
|
2542
2555
|
case "integer":
|
2543
2556
|
case "smallint":
|
2544
2557
|
case "bigint":
|
2558
|
+
case "serial":
|
2559
|
+
case "bigserial":
|
2545
2560
|
return isUpdate ? "42" : "1";
|
2546
2561
|
case "decimal":
|
2547
2562
|
case "numeric":
|
2548
2563
|
case "real":
|
2549
2564
|
case "double precision":
|
2550
2565
|
case "float":
|
2566
|
+
case "float4":
|
2567
|
+
case "float8":
|
2568
|
+
case "money":
|
2551
2569
|
return isUpdate ? "99.99" : "10.50";
|
2552
2570
|
case "boolean":
|
2553
2571
|
case "bool":
|
2554
2572
|
return isUpdate ? "false" : "true";
|
2555
2573
|
case "date":
|
2556
|
-
return `'2024-01-01'`;
|
2557
2574
|
case "timestamp":
|
2558
2575
|
case "timestamptz":
|
2576
|
+
case "timestamp without time zone":
|
2577
|
+
case "timestamp with time zone":
|
2578
|
+
case "time":
|
2579
|
+
case "timetz":
|
2580
|
+
case "time without time zone":
|
2581
|
+
case "time with time zone":
|
2559
2582
|
return `new Date()`;
|
2583
|
+
case "interval":
|
2584
|
+
return `'1 day'`;
|
2560
2585
|
case "json":
|
2561
2586
|
case "jsonb":
|
2562
|
-
return `{
|
2587
|
+
return `{}`;
|
2563
2588
|
case "uuid":
|
2564
|
-
return `'${
|
2565
|
-
case "
|
2566
|
-
|
2567
|
-
|
2589
|
+
return `'${generateUUID()}'`;
|
2590
|
+
case "inet":
|
2591
|
+
return `'192.168.1.1'`;
|
2592
|
+
case "cidr":
|
2593
|
+
return `'192.168.1.0/24'`;
|
2594
|
+
case "macaddr":
|
2595
|
+
case "macaddr8":
|
2596
|
+
return `'08:00:2b:01:02:03'`;
|
2597
|
+
case "point":
|
2598
|
+
return `'(1,2)'`;
|
2599
|
+
case "line":
|
2600
|
+
return `'{1,2,3}'`;
|
2601
|
+
case "lseg":
|
2602
|
+
return `'[(0,0),(1,1)]'`;
|
2603
|
+
case "box":
|
2604
|
+
return `'((0,0),(1,1))'`;
|
2605
|
+
case "path":
|
2606
|
+
return `'[(0,0),(1,1),(2,0)]'`;
|
2607
|
+
case "polygon":
|
2608
|
+
return `'((0,0),(1,1),(1,0))'`;
|
2609
|
+
case "circle":
|
2610
|
+
return `'<(0,0),1>'`;
|
2611
|
+
case "bit":
|
2612
|
+
case "bit varying":
|
2613
|
+
case "varbit":
|
2614
|
+
return `'101'`;
|
2615
|
+
case "bytea":
|
2616
|
+
return `'\\\\x0102'`;
|
2617
|
+
case "xml":
|
2618
|
+
return `'<root/>'`;
|
2619
|
+
case "tsvector":
|
2620
|
+
return `'a fat cat'`;
|
2621
|
+
case "tsquery":
|
2622
|
+
return `'fat & cat'`;
|
2623
|
+
case "oid":
|
2624
|
+
case "regproc":
|
2625
|
+
case "regprocedure":
|
2626
|
+
case "regoper":
|
2627
|
+
case "regoperator":
|
2628
|
+
case "regclass":
|
2629
|
+
case "regtype":
|
2630
|
+
case "regconfig":
|
2631
|
+
case "regdictionary":
|
2632
|
+
return "1";
|
2568
2633
|
default:
|
2569
|
-
|
2634
|
+
if (type.endsWith("[]") || type.startsWith("_")) {
|
2635
|
+
return `[]`;
|
2636
|
+
}
|
2637
|
+
return `'value1'`;
|
2570
2638
|
}
|
2571
2639
|
}
|
2640
|
+
function generateUUID() {
|
2641
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
2642
|
+
const r = Math.random() * 16 | 0;
|
2643
|
+
const v = c === "x" ? r : r & 3 | 8;
|
2644
|
+
return v.toString(16);
|
2645
|
+
});
|
2646
|
+
}
|
2572
2647
|
function generateTestCases(table, sampleData, updateData, hasForeignKeys = false) {
|
2573
2648
|
const Type = pascal(table.name);
|
2574
2649
|
const hasData = sampleData !== "{}";
|
2650
|
+
const isJunctionTable = table.pk.length > 1 && table.columns.every((col) => table.pk.includes(col.name) || col.name.endsWith("_id"));
|
2651
|
+
if (isJunctionTable) {
|
2652
|
+
return `it('should create a ${table.name} relationship', async () => {
|
2653
|
+
// This is a junction table for M:N relationships
|
2654
|
+
// Test data depends on parent records created in other tests
|
2655
|
+
expect(true).toBe(true);
|
2656
|
+
});
|
2657
|
+
|
2658
|
+
it('should list ${table.name} relationships', async () => {
|
2659
|
+
const list = await sdk.${table.name}.list({ limit: 10 });
|
2660
|
+
expect(Array.isArray(list)).toBe(true);
|
2661
|
+
});`;
|
2662
|
+
}
|
2575
2663
|
return `it('should create a ${table.name}', async () => {
|
2576
2664
|
const data: Insert${Type} = ${sampleData};
|
2577
2665
|
${hasData ? `
|