From 3895ee74ce490250fe84607b539e943504a380a7 Mon Sep 17 00:00:00 2001 From: Daniel Castro Date: Thu, 1 Dec 2022 16:12:06 +0000 Subject: [PATCH] add tests for correct schemas and fix naming --- .../function/static_error_disabled.pgsql | 21 + ....json => static_error_disabled.pgsql.json} | 0 ...error_trigger_column_does_not_exist.pgsql} | 0 ...r_trigger_column_does_not_exist.pgsql.json | 403 ++++++++++++++++++ .../schemas/correct_schema_1.sql | 0 .../schemas/correct_schema_2.sql | 0 .../schemas/correct_schema_3.sql | 0 server/src/__tests__/__fixtures__/schemas | 1 + server/src/__tests__/helpers/file.ts | 2 +- server/src/services/validation.test.ts | 29 +- 10 files changed, 453 insertions(+), 3 deletions(-) create mode 100644 sample/definitions/function/static_error_disabled.pgsql rename sample/definitions/function/{syntax_error_trigger_column_does_not_exist.pgsql.json => static_error_disabled.pgsql.json} (100%) rename sample/definitions/function/{syntax_error_trigger_column_does_not_exist.pgsql => static_error_trigger_column_does_not_exist.pgsql} (100%) create mode 100644 sample/definitions/function/static_error_trigger_column_does_not_exist.pgsql.json rename server/src/__tests__/__fixtures__/schemas/correct_schema_1.pgsql => sample/schemas/correct_schema_1.sql (100%) rename server/src/__tests__/__fixtures__/schemas/correct_schema_2.pgsql => sample/schemas/correct_schema_2.sql (100%) rename server/src/__tests__/__fixtures__/schemas/correct_schema_3.pgsql => sample/schemas/correct_schema_3.sql (100%) create mode 120000 server/src/__tests__/__fixtures__/schemas diff --git a/sample/definitions/function/static_error_disabled.pgsql b/sample/definitions/function/static_error_disabled.pgsql new file mode 100644 index 0000000..10f858b --- /dev/null +++ b/sample/definitions/function/static_error_disabled.pgsql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS users_2 CASCADE; + +CREATE TABLE users_2 ( + id integer not null PRIMARY KEY +); + +create or replace function update_updated_at_column () + returns trigger + language plpgsql + as $function$ +begin + new.updated_at = NOW(); + return new; +end; +$function$; + +-- TODO some kind of flag for disabling static analysis only per statement +-- plpgsql-language-server:disable-static +create trigger update_users_2_modtime -- should raise error + before update on users_2 for each row + execute function update_updated_at_column (); diff --git a/sample/definitions/function/syntax_error_trigger_column_does_not_exist.pgsql.json b/sample/definitions/function/static_error_disabled.pgsql.json similarity index 100% rename from sample/definitions/function/syntax_error_trigger_column_does_not_exist.pgsql.json rename to sample/definitions/function/static_error_disabled.pgsql.json diff --git a/sample/definitions/function/syntax_error_trigger_column_does_not_exist.pgsql b/sample/definitions/function/static_error_trigger_column_does_not_exist.pgsql similarity index 100% rename from sample/definitions/function/syntax_error_trigger_column_does_not_exist.pgsql rename to sample/definitions/function/static_error_trigger_column_does_not_exist.pgsql diff --git a/sample/definitions/function/static_error_trigger_column_does_not_exist.pgsql.json b/sample/definitions/function/static_error_trigger_column_does_not_exist.pgsql.json new file mode 100644 index 0000000..2adb046 --- /dev/null +++ b/sample/definitions/function/static_error_trigger_column_does_not_exist.pgsql.json @@ -0,0 +1,403 @@ +[ + { + "RawStmt": { + "stmt": { + "DropStmt": { + "objects": [ + { + "List": { + "items": [ + { + "String": { + "str": "users_1" + } + } + ] + } + } + ], + "removeType": "OBJECT_TABLE", + "behavior": "DROP_CASCADE", + "missing_ok": true + } + }, + "stmt_len": 36 + } + }, + { + "RawStmt": { + "stmt": { + "DropStmt": { + "objects": [ + { + "List": { + "items": [ + { + "String": { + "str": "users_2" + } + } + ] + } + } + ], + "removeType": "OBJECT_TABLE", + "behavior": "DROP_CASCADE", + "missing_ok": true + } + }, + "stmt_len": 37 + } + }, + { + "RawStmt": { + "stmt": { + "DropStmt": { + "objects": [ + { + "List": { + "items": [ + { + "String": { + "str": "users_3" + } + } + ] + } + } + ], + "removeType": "OBJECT_TABLE", + "behavior": "DROP_CASCADE", + "missing_ok": true + } + }, + "stmt_len": 37 + } + }, + { + "RawStmt": { + "stmt": { + "CreateStmt": { + "relation": { + "relname": "users_1", + "inh": true, + "relpersistence": "p" + }, + "tableElts": [ + { + "ColumnDef": { + "colname": "id", + "typeName": { + "names": [ + { + "String": { + "str": "pg_catalog" + } + }, + { + "String": { + "str": "int4" + } + } + ], + "typemod": -1 + }, + "is_local": true, + "constraints": [ + { + "Constraint": { + "contype": "CONSTR_NOTNULL" + } + }, + { + "Constraint": { + "contype": "CONSTR_PRIMARY" + } + } + ] + } + }, + { + "ColumnDef": { + "colname": "updated_at", + "typeName": { + "names": [ + { + "String": { + "str": "pg_catalog" + } + }, + { + "String": { + "str": "timestamptz" + } + } + ], + "typemod": -1 + }, + "is_local": true, + "constraints": [ + { + "Constraint": { + "contype": "CONSTR_NOTNULL" + } + }, + { + "Constraint": { + "contype": "CONSTR_DEFAULT", + "raw_expr": { + "FuncCall": { + "funcname": [ + { + "String": { + "str": "now" + } + } + ] + } + } + } + } + ] + } + } + ], + "oncommit": "ONCOMMIT_NOOP" + } + }, + "stmt_len": 122 + } + }, + { + "RawStmt": { + "stmt": { + "CreateStmt": { + "relation": { + "relname": "users_2", + "inh": true, + "relpersistence": "p" + }, + "tableElts": [ + { + "ColumnDef": { + "colname": "id", + "typeName": { + "names": [ + { + "String": { + "str": "pg_catalog" + } + }, + { + "String": { + "str": "int4" + } + } + ], + "typemod": -1 + }, + "is_local": true, + "constraints": [ + { + "Constraint": { + "contype": "CONSTR_NOTNULL" + } + }, + { + "Constraint": { + "contype": "CONSTR_PRIMARY" + } + } + ] + } + } + ], + "oncommit": "ONCOMMIT_NOOP" + } + }, + "stmt_len": 59 + } + }, + { + "RawStmt": { + "stmt": { + "CreateStmt": { + "relation": { + "relname": "users_3", + "inh": true, + "relpersistence": "p" + }, + "tableElts": [ + { + "ColumnDef": { + "colname": "id", + "typeName": { + "names": [ + { + "String": { + "str": "pg_catalog" + } + }, + { + "String": { + "str": "int4" + } + } + ], + "typemod": -1 + }, + "is_local": true, + "constraints": [ + { + "Constraint": { + "contype": "CONSTR_NOTNULL" + } + }, + { + "Constraint": { + "contype": "CONSTR_PRIMARY" + } + } + ] + } + } + ], + "oncommit": "ONCOMMIT_NOOP" + } + }, + "stmt_len": 59 + } + }, + { + "RawStmt": { + "stmt": { + "CreateFunctionStmt": { + "replace": true, + "funcname": [ + { + "String": { + "str": "update_updated_at_column" + } + } + ], + "returnType": { + "names": [ + { + "String": { + "str": "trigger" + } + } + ], + "typemod": -1 + }, + "options": [ + { + "DefElem": { + "defname": "language", + "arg": { + "String": { + "str": "plpgsql" + } + }, + "defaction": "DEFELEM_UNSPEC" + } + }, + { + "DefElem": { + "defname": "as", + "arg": { + "List": { + "items": [ + { + "String": { + "str": "\nbegin\n new.updated_at = NOW();\n return new;\nend;\n" + } + } + ] + } + }, + "defaction": "DEFELEM_UNSPEC" + } + } + ] + } + }, + "stmt_len": 171 + } + }, + { + "RawStmt": { + "stmt": { + "CreateTrigStmt": { + "trigname": "update_users_3_modtime", + "relation": { + "relname": "users_3", + "inh": true, + "relpersistence": "p" + }, + "funcname": [ + { + "String": { + "str": "update_updated_at_column" + } + } + ], + "row": true, + "timing": 2, + "events": 16 + } + }, + "stmt_len": 190 + } + }, + { + "RawStmt": { + "stmt": { + "CreateTrigStmt": { + "trigname": "update_users_1_modtime", + "relation": { + "relname": "users_1", + "inh": true, + "relpersistence": "p" + }, + "funcname": [ + { + "String": { + "str": "update_updated_at_column" + } + } + ], + "row": true, + "timing": 2, + "events": 16 + } + }, + "stmt_len": 126 + } + }, + { + "RawStmt": { + "stmt": { + "CreateTrigStmt": { + "trigname": "update_users_2_modtime", + "relation": { + "relname": "users_2", + "inh": true, + "relpersistence": "p" + }, + "funcname": [ + { + "String": { + "str": "update_updated_at_column" + } + } + ], + "row": true, + "timing": 2, + "events": 16 + } + }, + "stmt_len": 148 + } + } +] \ No newline at end of file diff --git a/server/src/__tests__/__fixtures__/schemas/correct_schema_1.pgsql b/sample/schemas/correct_schema_1.sql similarity index 100% rename from server/src/__tests__/__fixtures__/schemas/correct_schema_1.pgsql rename to sample/schemas/correct_schema_1.sql diff --git a/server/src/__tests__/__fixtures__/schemas/correct_schema_2.pgsql b/sample/schemas/correct_schema_2.sql similarity index 100% rename from server/src/__tests__/__fixtures__/schemas/correct_schema_2.pgsql rename to sample/schemas/correct_schema_2.sql diff --git a/server/src/__tests__/__fixtures__/schemas/correct_schema_3.pgsql b/sample/schemas/correct_schema_3.sql similarity index 100% rename from server/src/__tests__/__fixtures__/schemas/correct_schema_3.pgsql rename to sample/schemas/correct_schema_3.sql diff --git a/server/src/__tests__/__fixtures__/schemas b/server/src/__tests__/__fixtures__/schemas new file mode 120000 index 0000000..e28eb7d --- /dev/null +++ b/server/src/__tests__/__fixtures__/schemas @@ -0,0 +1 @@ +../../../../sample/schemas/ \ No newline at end of file diff --git a/server/src/__tests__/helpers/file.ts b/server/src/__tests__/helpers/file.ts index 99d4e01..c760985 100644 --- a/server/src/__tests__/helpers/file.ts +++ b/server/src/__tests__/helpers/file.ts @@ -37,7 +37,7 @@ export async function loadSampleFile( } } -function sampleDirPath(): string { +export function sampleDirPath(): string { return path.join(__dirname, "..", "__fixtures__") } diff --git a/server/src/services/validation.test.ts b/server/src/services/validation.test.ts index 5045e40..bd10570 100644 --- a/server/src/services/validation.test.ts +++ b/server/src/services/validation.test.ts @@ -1,6 +1,10 @@ +import glob from "glob-promise" +import path from "path" import { Diagnostic, DiagnosticSeverity, Range } from "vscode-languageserver" -import { DEFAULT_LOAD_FILE_OPTIONS, LoadFileOptions } from "@/__tests__/helpers/file" +import { + DEFAULT_LOAD_FILE_OPTIONS, LoadFileOptions, sampleDirPath, +} from "@/__tests__/helpers/file" import { RecordLogger } from "@/__tests__/helpers/logger" import { setupTestServer } from "@/__tests__/helpers/server" import { SettingsBuilder } from "@/__tests__/helpers/settings" @@ -70,7 +74,7 @@ describe("Validate Tests", () => { it("TRIGGER on inexistent field", async () => { const diagnostics = await validateSampleFile( - "definitions/function/syntax_error_trigger_column_does_not_exist.pgsql", + "definitions/function/static_error_trigger_column_does_not_exist.pgsql", ) expect(diagnostics).toStrictEqual([ @@ -86,6 +90,15 @@ describe("Validate Tests", () => { ]) }) + // TODO + it.skip("static analysis disabled on invalid statement", async () => { + const diagnostics = await validateSampleFile( + "definitions/function/static_error_disabled.pgsql", + ) + + expect(diagnostics).toStrictEqual([]) + }) + it("FUNCTION column does not exists", async () => { const diagnostics = await validateSampleFile( "definitions/function/syntax_error_function_column_does_not_exist.pgsql", @@ -109,6 +122,18 @@ describe("Validate Tests", () => { expect(diagnostics).toStrictEqual([]) }) + + it("correct schemas", async () => { + const schemas = (await glob.promise(path.join(sampleDirPath(), "schemas/*.sql"))) + .map(file => path.relative(sampleDirPath(), file)) + + schemas.forEach(async (schema) => { + const diagnostics = await validateSampleFile(schema) + + expect(diagnostics).toStrictEqual([]) + }) + }) + it("Syntax error query", async () => { const diagnostics = await validateSampleFile( "queries/syntax_error_query_with_language_server_disable_comment.pgsql",