postgresdk 0.6.6 → 0.6.7
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 +222 -115
- package/dist/index.js +222 -115
- package/package.json +1 -1
package/dist/cli.js
CHANGED
@@ -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,43 @@ 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
|
-
if (col.
|
2704
|
-
|
2705
|
-
|
2706
|
-
|
2707
|
-
|
2735
|
+
if (col.hasDefault) {
|
2736
|
+
const autoGenerated = ["id", "created_at", "updated_at", "created", "updated", "modified_at"];
|
2737
|
+
if (autoGenerated.includes(col.name.toLowerCase())) {
|
2738
|
+
continue;
|
2739
|
+
}
|
2708
2740
|
}
|
2709
|
-
if (col.name === "deleted_at") {
|
2741
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2710
2742
|
continue;
|
2711
2743
|
}
|
2712
2744
|
if (!col.nullable) {
|
2713
2745
|
const foreignTable = foreignKeyColumns.get(col.name);
|
2714
|
-
|
2715
|
-
|
2746
|
+
if (foreignTable) {
|
2747
|
+
fields.push(` ${col.name}: ${foreignTable}Id`);
|
2748
|
+
} else {
|
2749
|
+
const value = generateValueForColumn(col);
|
2750
|
+
fields.push(` ${col.name}: ${value}`);
|
2751
|
+
}
|
2716
2752
|
} else {
|
2717
|
-
const
|
2718
|
-
if (
|
2719
|
-
|
2720
|
-
|
2753
|
+
const foreignTable = foreignKeyColumns.get(col.name);
|
2754
|
+
if (foreignTable) {
|
2755
|
+
fields.push(` ${col.name}: ${foreignTable}Id`);
|
2756
|
+
} else if (shouldIncludeNullableColumn(col)) {
|
2757
|
+
const value = generateValueForColumn(col);
|
2721
2758
|
fields.push(` ${col.name}: ${value}`);
|
2722
2759
|
}
|
2723
2760
|
}
|
@@ -2727,14 +2764,23 @@ ${fields.join(`,
|
|
2727
2764
|
`)}
|
2728
2765
|
}` : "{}";
|
2729
2766
|
}
|
2730
|
-
function
|
2767
|
+
function generateUpdateDataFromSchema(table) {
|
2731
2768
|
const fields = [];
|
2732
2769
|
for (const col of table.columns) {
|
2733
|
-
if (
|
2770
|
+
if (table.pk.includes(col.name) || col.hasDefault) {
|
2771
|
+
const autoGenerated = ["id", "created_at", "updated_at", "created", "updated", "modified_at"];
|
2772
|
+
if (autoGenerated.includes(col.name.toLowerCase())) {
|
2773
|
+
continue;
|
2774
|
+
}
|
2775
|
+
}
|
2776
|
+
if (col.name.endsWith("_id")) {
|
2734
2777
|
continue;
|
2735
2778
|
}
|
2736
|
-
if (
|
2737
|
-
|
2779
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2780
|
+
continue;
|
2781
|
+
}
|
2782
|
+
if (!col.nullable || shouldIncludeNullableColumn(col)) {
|
2783
|
+
const value = generateValueForColumn(col, true);
|
2738
2784
|
fields.push(` ${col.name}: ${value}`);
|
2739
2785
|
break;
|
2740
2786
|
}
|
@@ -2744,71 +2790,107 @@ ${fields.join(`,
|
|
2744
2790
|
`)}
|
2745
2791
|
}` : "{}";
|
2746
2792
|
}
|
2747
|
-
function
|
2748
|
-
const
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
2793
|
+
function shouldIncludeNullableColumn(col) {
|
2794
|
+
const importantPatterns = [
|
2795
|
+
"_id",
|
2796
|
+
"_by",
|
2797
|
+
"email",
|
2798
|
+
"name",
|
2799
|
+
"title",
|
2800
|
+
"description",
|
2801
|
+
"phone",
|
2802
|
+
"address",
|
2803
|
+
"status",
|
2804
|
+
"type",
|
2805
|
+
"category",
|
2806
|
+
"price",
|
2807
|
+
"amount",
|
2808
|
+
"quantity",
|
2809
|
+
"url",
|
2810
|
+
"slug"
|
2811
|
+
];
|
2812
|
+
const name = col.name.toLowerCase();
|
2813
|
+
return importantPatterns.some((pattern) => name.includes(pattern));
|
2814
|
+
}
|
2815
|
+
function generateValueForColumn(col, isUpdate = false) {
|
2816
|
+
const name = col.name.toLowerCase();
|
2817
|
+
const type = col.pgType.toLowerCase();
|
2752
2818
|
if (name.includes("email")) {
|
2753
2819
|
return `'test${isUpdate ? ".updated" : ""}@example.com'`;
|
2754
2820
|
}
|
2755
|
-
if (name
|
2756
|
-
return `'
|
2821
|
+
if (name.includes("phone")) {
|
2822
|
+
return `'555-${isUpdate ? "0200" : "0100"}'`;
|
2757
2823
|
}
|
2758
|
-
if (name
|
2759
|
-
return `'${isUpdate ? "
|
2824
|
+
if (name.includes("url") || name.includes("website")) {
|
2825
|
+
return `'https://example.com${isUpdate ? "/updated" : ""}'`;
|
2760
2826
|
}
|
2761
|
-
if (name.includes("
|
2762
|
-
return `'
|
2827
|
+
if (name.includes("password")) {
|
2828
|
+
return `'hashedPassword123'`;
|
2763
2829
|
}
|
2764
|
-
if (name.includes("
|
2765
|
-
return `'
|
2830
|
+
if (name === "name" || name.includes("_name") || name.includes("name_")) {
|
2831
|
+
return `'Test ${pascal(col.name)}${isUpdate ? " Updated" : ""}'`;
|
2766
2832
|
}
|
2767
|
-
if (name === "
|
2768
|
-
return `'${isUpdate ? "
|
2833
|
+
if (name === "title" || name.includes("title")) {
|
2834
|
+
return `'Test Title${isUpdate ? " Updated" : ""}'`;
|
2769
2835
|
}
|
2770
|
-
if (name === "
|
2771
|
-
return `'Test
|
2836
|
+
if (name.includes("description") || name === "bio" || name === "about") {
|
2837
|
+
return `'Test description${isUpdate ? " updated" : ""}'`;
|
2772
2838
|
}
|
2773
|
-
if (name
|
2774
|
-
return `'
|
2839
|
+
if (name === "slug") {
|
2840
|
+
return `'test-slug${isUpdate ? "-updated" : ""}'`;
|
2775
2841
|
}
|
2776
|
-
if (name
|
2777
|
-
return `'
|
2842
|
+
if (name === "status") {
|
2843
|
+
return `'${isUpdate ? "updated" : "active"}'`;
|
2778
2844
|
}
|
2779
|
-
if (name
|
2780
|
-
return `'
|
2845
|
+
if (name === "type" || name === "kind" || name === "category") {
|
2846
|
+
return `'${isUpdate ? "type2" : "type1"}'`;
|
2781
2847
|
}
|
2782
|
-
if (name
|
2783
|
-
return `
|
2848
|
+
if (name === "color" || name === "colour") {
|
2849
|
+
return `'${isUpdate ? "#FF0000" : "#0000FF"}'`;
|
2784
2850
|
}
|
2785
|
-
if (name
|
2786
|
-
return `'
|
2851
|
+
if (name === "gender") {
|
2852
|
+
return `'${isUpdate ? "F" : "M"}'`;
|
2787
2853
|
}
|
2788
|
-
if (name.includes("
|
2789
|
-
return
|
2854
|
+
if (name.includes("price") || name === "cost" || name === "amount") {
|
2855
|
+
return isUpdate ? "99.99" : "10.50";
|
2790
2856
|
}
|
2791
|
-
if (name.includes("
|
2792
|
-
return
|
2857
|
+
if (name === "quantity" || name === "count" || name.includes("qty")) {
|
2858
|
+
return isUpdate ? "5" : "1";
|
2793
2859
|
}
|
2794
|
-
if (name
|
2795
|
-
return
|
2860
|
+
if (name === "age") {
|
2861
|
+
return isUpdate ? "30" : "25";
|
2796
2862
|
}
|
2797
|
-
if (name === "
|
2798
|
-
return
|
2863
|
+
if (name.includes("percent") || name === "rate" || name === "ratio") {
|
2864
|
+
return isUpdate ? "0.75" : "0.5";
|
2799
2865
|
}
|
2800
|
-
if (name.includes("
|
2801
|
-
return
|
2866
|
+
if (name.includes("latitude") || name === "lat") {
|
2867
|
+
return "40.7128";
|
2802
2868
|
}
|
2803
|
-
if (name.includes("
|
2804
|
-
return
|
2869
|
+
if (name.includes("longitude") || name === "lng" || name === "lon") {
|
2870
|
+
return "-74.0060";
|
2871
|
+
}
|
2872
|
+
if (type.includes("date") || type.includes("timestamp")) {
|
2873
|
+
if (name.includes("birth") || name === "dob") {
|
2874
|
+
return `new Date('1990-01-01')`;
|
2875
|
+
}
|
2876
|
+
if (name.includes("end") || name.includes("expire")) {
|
2877
|
+
return `new Date('2025-12-31')`;
|
2878
|
+
}
|
2879
|
+
if (name.includes("start") || name.includes("begin")) {
|
2880
|
+
return `new Date('2024-01-01')`;
|
2881
|
+
}
|
2882
|
+
return `new Date()`;
|
2805
2883
|
}
|
2806
2884
|
switch (type) {
|
2807
2885
|
case "text":
|
2808
2886
|
case "varchar":
|
2809
2887
|
case "char":
|
2810
|
-
|
2888
|
+
case "character varying":
|
2889
|
+
return `'test_value${isUpdate ? "_updated" : ""}'`;
|
2811
2890
|
case "int":
|
2891
|
+
case "int2":
|
2892
|
+
case "int4":
|
2893
|
+
case "int8":
|
2812
2894
|
case "integer":
|
2813
2895
|
case "smallint":
|
2814
2896
|
case "bigint":
|
@@ -2818,30 +2900,55 @@ function getSampleValue(type, name, isUpdate = false) {
|
|
2818
2900
|
case "real":
|
2819
2901
|
case "double precision":
|
2820
2902
|
case "float":
|
2903
|
+
case "float4":
|
2904
|
+
case "float8":
|
2821
2905
|
return isUpdate ? "99.99" : "10.50";
|
2822
2906
|
case "boolean":
|
2823
2907
|
case "bool":
|
2824
2908
|
return isUpdate ? "false" : "true";
|
2825
|
-
case "date":
|
2826
|
-
return `'2024-01-01'`;
|
2827
|
-
case "timestamp":
|
2828
|
-
case "timestamptz":
|
2829
|
-
return `new Date()`;
|
2830
2909
|
case "json":
|
2831
2910
|
case "jsonb":
|
2832
|
-
return `{ key: 'value' }`;
|
2911
|
+
return `{ key: '${isUpdate ? "updated" : "value"}' }`;
|
2833
2912
|
case "uuid":
|
2834
2913
|
return `'${isUpdate ? "550e8400-e29b-41d4-a716-446655440001" : "550e8400-e29b-41d4-a716-446655440000"}'`;
|
2835
|
-
case "
|
2836
|
-
|
2837
|
-
|
2914
|
+
case "inet":
|
2915
|
+
return `'${isUpdate ? "192.168.1.2" : "192.168.1.1"}'`;
|
2916
|
+
case "cidr":
|
2917
|
+
return `'192.168.1.0/24'`;
|
2918
|
+
case "macaddr":
|
2919
|
+
return `'08:00:2b:01:02:0${isUpdate ? "4" : "3"}'`;
|
2920
|
+
case "xml":
|
2921
|
+
return `'<root>${isUpdate ? "updated" : "value"}</root>'`;
|
2838
2922
|
default:
|
2839
|
-
|
2923
|
+
if (type.endsWith("[]")) {
|
2924
|
+
const baseType = type.slice(0, -2);
|
2925
|
+
if (baseType === "text" || baseType === "varchar") {
|
2926
|
+
return `['item1', 'item2${isUpdate ? "_updated" : ""}']`;
|
2927
|
+
}
|
2928
|
+
if (baseType === "int" || baseType === "integer") {
|
2929
|
+
return `[1, 2, ${isUpdate ? "3" : ""}]`;
|
2930
|
+
}
|
2931
|
+
return `[]`;
|
2932
|
+
}
|
2933
|
+
return `'test${isUpdate ? "_updated" : ""}'`;
|
2840
2934
|
}
|
2841
2935
|
}
|
2842
2936
|
function generateTestCases(table, sampleData, updateData, hasForeignKeys = false) {
|
2843
2937
|
const Type = pascal(table.name);
|
2844
2938
|
const hasData = sampleData !== "{}";
|
2939
|
+
const isJunctionTable = table.pk.length > 1 && table.columns.every((col) => table.pk.includes(col.name) || col.name.endsWith("_id"));
|
2940
|
+
if (isJunctionTable) {
|
2941
|
+
return `it('should create a ${table.name} relationship', async () => {
|
2942
|
+
// This is a junction table for M:N relationships
|
2943
|
+
// Test data depends on parent records created in other tests
|
2944
|
+
expect(true).toBe(true);
|
2945
|
+
});
|
2946
|
+
|
2947
|
+
it('should list ${table.name} relationships', async () => {
|
2948
|
+
const list = await sdk.${table.name}.list({ limit: 10 });
|
2949
|
+
expect(Array.isArray(list)).toBe(true);
|
2950
|
+
});`;
|
2951
|
+
}
|
2845
2952
|
return `it('should create a ${table.name}', async () => {
|
2846
2953
|
const data: Insert${Type} = ${sampleData};
|
2847
2954
|
${hasData ? `
|
package/dist/index.js
CHANGED
@@ -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,43 @@ 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
|
-
if (col.
|
2434
|
-
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2465
|
+
if (col.hasDefault) {
|
2466
|
+
const autoGenerated = ["id", "created_at", "updated_at", "created", "updated", "modified_at"];
|
2467
|
+
if (autoGenerated.includes(col.name.toLowerCase())) {
|
2468
|
+
continue;
|
2469
|
+
}
|
2438
2470
|
}
|
2439
|
-
if (col.name === "deleted_at") {
|
2471
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2440
2472
|
continue;
|
2441
2473
|
}
|
2442
2474
|
if (!col.nullable) {
|
2443
2475
|
const foreignTable = foreignKeyColumns.get(col.name);
|
2444
|
-
|
2445
|
-
|
2476
|
+
if (foreignTable) {
|
2477
|
+
fields.push(` ${col.name}: ${foreignTable}Id`);
|
2478
|
+
} else {
|
2479
|
+
const value = generateValueForColumn(col);
|
2480
|
+
fields.push(` ${col.name}: ${value}`);
|
2481
|
+
}
|
2446
2482
|
} else {
|
2447
|
-
const
|
2448
|
-
if (
|
2449
|
-
|
2450
|
-
|
2483
|
+
const foreignTable = foreignKeyColumns.get(col.name);
|
2484
|
+
if (foreignTable) {
|
2485
|
+
fields.push(` ${col.name}: ${foreignTable}Id`);
|
2486
|
+
} else if (shouldIncludeNullableColumn(col)) {
|
2487
|
+
const value = generateValueForColumn(col);
|
2451
2488
|
fields.push(` ${col.name}: ${value}`);
|
2452
2489
|
}
|
2453
2490
|
}
|
@@ -2457,14 +2494,23 @@ ${fields.join(`,
|
|
2457
2494
|
`)}
|
2458
2495
|
}` : "{}";
|
2459
2496
|
}
|
2460
|
-
function
|
2497
|
+
function generateUpdateDataFromSchema(table) {
|
2461
2498
|
const fields = [];
|
2462
2499
|
for (const col of table.columns) {
|
2463
|
-
if (
|
2500
|
+
if (table.pk.includes(col.name) || col.hasDefault) {
|
2501
|
+
const autoGenerated = ["id", "created_at", "updated_at", "created", "updated", "modified_at"];
|
2502
|
+
if (autoGenerated.includes(col.name.toLowerCase())) {
|
2503
|
+
continue;
|
2504
|
+
}
|
2505
|
+
}
|
2506
|
+
if (col.name.endsWith("_id")) {
|
2464
2507
|
continue;
|
2465
2508
|
}
|
2466
|
-
if (
|
2467
|
-
|
2509
|
+
if (col.name === "deleted_at" || col.name === "deleted") {
|
2510
|
+
continue;
|
2511
|
+
}
|
2512
|
+
if (!col.nullable || shouldIncludeNullableColumn(col)) {
|
2513
|
+
const value = generateValueForColumn(col, true);
|
2468
2514
|
fields.push(` ${col.name}: ${value}`);
|
2469
2515
|
break;
|
2470
2516
|
}
|
@@ -2474,71 +2520,107 @@ ${fields.join(`,
|
|
2474
2520
|
`)}
|
2475
2521
|
}` : "{}";
|
2476
2522
|
}
|
2477
|
-
function
|
2478
|
-
const
|
2479
|
-
|
2480
|
-
|
2481
|
-
|
2523
|
+
function shouldIncludeNullableColumn(col) {
|
2524
|
+
const importantPatterns = [
|
2525
|
+
"_id",
|
2526
|
+
"_by",
|
2527
|
+
"email",
|
2528
|
+
"name",
|
2529
|
+
"title",
|
2530
|
+
"description",
|
2531
|
+
"phone",
|
2532
|
+
"address",
|
2533
|
+
"status",
|
2534
|
+
"type",
|
2535
|
+
"category",
|
2536
|
+
"price",
|
2537
|
+
"amount",
|
2538
|
+
"quantity",
|
2539
|
+
"url",
|
2540
|
+
"slug"
|
2541
|
+
];
|
2542
|
+
const name = col.name.toLowerCase();
|
2543
|
+
return importantPatterns.some((pattern) => name.includes(pattern));
|
2544
|
+
}
|
2545
|
+
function generateValueForColumn(col, isUpdate = false) {
|
2546
|
+
const name = col.name.toLowerCase();
|
2547
|
+
const type = col.pgType.toLowerCase();
|
2482
2548
|
if (name.includes("email")) {
|
2483
2549
|
return `'test${isUpdate ? ".updated" : ""}@example.com'`;
|
2484
2550
|
}
|
2485
|
-
if (name
|
2486
|
-
return `'
|
2551
|
+
if (name.includes("phone")) {
|
2552
|
+
return `'555-${isUpdate ? "0200" : "0100"}'`;
|
2487
2553
|
}
|
2488
|
-
if (name
|
2489
|
-
return `'${isUpdate ? "
|
2554
|
+
if (name.includes("url") || name.includes("website")) {
|
2555
|
+
return `'https://example.com${isUpdate ? "/updated" : ""}'`;
|
2490
2556
|
}
|
2491
|
-
if (name.includes("
|
2492
|
-
return `'
|
2557
|
+
if (name.includes("password")) {
|
2558
|
+
return `'hashedPassword123'`;
|
2493
2559
|
}
|
2494
|
-
if (name.includes("
|
2495
|
-
return `'
|
2560
|
+
if (name === "name" || name.includes("_name") || name.includes("name_")) {
|
2561
|
+
return `'Test ${pascal(col.name)}${isUpdate ? " Updated" : ""}'`;
|
2496
2562
|
}
|
2497
|
-
if (name === "
|
2498
|
-
return `'${isUpdate ? "
|
2563
|
+
if (name === "title" || name.includes("title")) {
|
2564
|
+
return `'Test Title${isUpdate ? " Updated" : ""}'`;
|
2499
2565
|
}
|
2500
|
-
if (name === "
|
2501
|
-
return `'Test
|
2566
|
+
if (name.includes("description") || name === "bio" || name === "about") {
|
2567
|
+
return `'Test description${isUpdate ? " updated" : ""}'`;
|
2502
2568
|
}
|
2503
|
-
if (name
|
2504
|
-
return `'
|
2569
|
+
if (name === "slug") {
|
2570
|
+
return `'test-slug${isUpdate ? "-updated" : ""}'`;
|
2505
2571
|
}
|
2506
|
-
if (name
|
2507
|
-
return `'
|
2572
|
+
if (name === "status") {
|
2573
|
+
return `'${isUpdate ? "updated" : "active"}'`;
|
2508
2574
|
}
|
2509
|
-
if (name
|
2510
|
-
return `'
|
2575
|
+
if (name === "type" || name === "kind" || name === "category") {
|
2576
|
+
return `'${isUpdate ? "type2" : "type1"}'`;
|
2511
2577
|
}
|
2512
|
-
if (name
|
2513
|
-
return `
|
2578
|
+
if (name === "color" || name === "colour") {
|
2579
|
+
return `'${isUpdate ? "#FF0000" : "#0000FF"}'`;
|
2514
2580
|
}
|
2515
|
-
if (name
|
2516
|
-
return `'
|
2581
|
+
if (name === "gender") {
|
2582
|
+
return `'${isUpdate ? "F" : "M"}'`;
|
2517
2583
|
}
|
2518
|
-
if (name.includes("
|
2519
|
-
return
|
2584
|
+
if (name.includes("price") || name === "cost" || name === "amount") {
|
2585
|
+
return isUpdate ? "99.99" : "10.50";
|
2520
2586
|
}
|
2521
|
-
if (name.includes("
|
2522
|
-
return
|
2587
|
+
if (name === "quantity" || name === "count" || name.includes("qty")) {
|
2588
|
+
return isUpdate ? "5" : "1";
|
2523
2589
|
}
|
2524
|
-
if (name
|
2525
|
-
return
|
2590
|
+
if (name === "age") {
|
2591
|
+
return isUpdate ? "30" : "25";
|
2526
2592
|
}
|
2527
|
-
if (name === "
|
2528
|
-
return
|
2593
|
+
if (name.includes("percent") || name === "rate" || name === "ratio") {
|
2594
|
+
return isUpdate ? "0.75" : "0.5";
|
2529
2595
|
}
|
2530
|
-
if (name.includes("
|
2531
|
-
return
|
2596
|
+
if (name.includes("latitude") || name === "lat") {
|
2597
|
+
return "40.7128";
|
2532
2598
|
}
|
2533
|
-
if (name.includes("
|
2534
|
-
return
|
2599
|
+
if (name.includes("longitude") || name === "lng" || name === "lon") {
|
2600
|
+
return "-74.0060";
|
2601
|
+
}
|
2602
|
+
if (type.includes("date") || type.includes("timestamp")) {
|
2603
|
+
if (name.includes("birth") || name === "dob") {
|
2604
|
+
return `new Date('1990-01-01')`;
|
2605
|
+
}
|
2606
|
+
if (name.includes("end") || name.includes("expire")) {
|
2607
|
+
return `new Date('2025-12-31')`;
|
2608
|
+
}
|
2609
|
+
if (name.includes("start") || name.includes("begin")) {
|
2610
|
+
return `new Date('2024-01-01')`;
|
2611
|
+
}
|
2612
|
+
return `new Date()`;
|
2535
2613
|
}
|
2536
2614
|
switch (type) {
|
2537
2615
|
case "text":
|
2538
2616
|
case "varchar":
|
2539
2617
|
case "char":
|
2540
|
-
|
2618
|
+
case "character varying":
|
2619
|
+
return `'test_value${isUpdate ? "_updated" : ""}'`;
|
2541
2620
|
case "int":
|
2621
|
+
case "int2":
|
2622
|
+
case "int4":
|
2623
|
+
case "int8":
|
2542
2624
|
case "integer":
|
2543
2625
|
case "smallint":
|
2544
2626
|
case "bigint":
|
@@ -2548,30 +2630,55 @@ function getSampleValue(type, name, isUpdate = false) {
|
|
2548
2630
|
case "real":
|
2549
2631
|
case "double precision":
|
2550
2632
|
case "float":
|
2633
|
+
case "float4":
|
2634
|
+
case "float8":
|
2551
2635
|
return isUpdate ? "99.99" : "10.50";
|
2552
2636
|
case "boolean":
|
2553
2637
|
case "bool":
|
2554
2638
|
return isUpdate ? "false" : "true";
|
2555
|
-
case "date":
|
2556
|
-
return `'2024-01-01'`;
|
2557
|
-
case "timestamp":
|
2558
|
-
case "timestamptz":
|
2559
|
-
return `new Date()`;
|
2560
2639
|
case "json":
|
2561
2640
|
case "jsonb":
|
2562
|
-
return `{ key: 'value' }`;
|
2641
|
+
return `{ key: '${isUpdate ? "updated" : "value"}' }`;
|
2563
2642
|
case "uuid":
|
2564
2643
|
return `'${isUpdate ? "550e8400-e29b-41d4-a716-446655440001" : "550e8400-e29b-41d4-a716-446655440000"}'`;
|
2565
|
-
case "
|
2566
|
-
|
2567
|
-
|
2644
|
+
case "inet":
|
2645
|
+
return `'${isUpdate ? "192.168.1.2" : "192.168.1.1"}'`;
|
2646
|
+
case "cidr":
|
2647
|
+
return `'192.168.1.0/24'`;
|
2648
|
+
case "macaddr":
|
2649
|
+
return `'08:00:2b:01:02:0${isUpdate ? "4" : "3"}'`;
|
2650
|
+
case "xml":
|
2651
|
+
return `'<root>${isUpdate ? "updated" : "value"}</root>'`;
|
2568
2652
|
default:
|
2569
|
-
|
2653
|
+
if (type.endsWith("[]")) {
|
2654
|
+
const baseType = type.slice(0, -2);
|
2655
|
+
if (baseType === "text" || baseType === "varchar") {
|
2656
|
+
return `['item1', 'item2${isUpdate ? "_updated" : ""}']`;
|
2657
|
+
}
|
2658
|
+
if (baseType === "int" || baseType === "integer") {
|
2659
|
+
return `[1, 2, ${isUpdate ? "3" : ""}]`;
|
2660
|
+
}
|
2661
|
+
return `[]`;
|
2662
|
+
}
|
2663
|
+
return `'test${isUpdate ? "_updated" : ""}'`;
|
2570
2664
|
}
|
2571
2665
|
}
|
2572
2666
|
function generateTestCases(table, sampleData, updateData, hasForeignKeys = false) {
|
2573
2667
|
const Type = pascal(table.name);
|
2574
2668
|
const hasData = sampleData !== "{}";
|
2669
|
+
const isJunctionTable = table.pk.length > 1 && table.columns.every((col) => table.pk.includes(col.name) || col.name.endsWith("_id"));
|
2670
|
+
if (isJunctionTable) {
|
2671
|
+
return `it('should create a ${table.name} relationship', async () => {
|
2672
|
+
// This is a junction table for M:N relationships
|
2673
|
+
// Test data depends on parent records created in other tests
|
2674
|
+
expect(true).toBe(true);
|
2675
|
+
});
|
2676
|
+
|
2677
|
+
it('should list ${table.name} relationships', async () => {
|
2678
|
+
const list = await sdk.${table.name}.list({ limit: 10 });
|
2679
|
+
expect(Array.isArray(list)).toBe(true);
|
2680
|
+
});`;
|
2681
|
+
}
|
2575
2682
|
return `it('should create a ${table.name}', async () => {
|
2576
2683
|
const data: Insert${Type} = ${sampleData};
|
2577
2684
|
${hasData ? `
|