ddl-manager
Version:
store postgres procedures and triggers in files
557 lines (453 loc) • 14.3 kB
text/typescript
import assert from "assert";
import fs from "fs";
import fse from "fs-extra";
import { getDBClient } from "../getDbClient";
import { DDLManager } from "../../../lib/DDLManager";
import {expect, use} from "chai";
import chaiShallowDeepEqualPlugin from "chai-shallow-deep-equal";
import { sleep } from "../sleep";
use(chaiShallowDeepEqualPlugin);
const ROOT_TMP_PATH = __dirname + "/tmp";
describe("integration/DDLManager.watch", () => {
let db: any;
const dbConfig = require("../../../ddl-manager-config");
beforeEach(async() => {
db = await getDBClient();
await db.query(`
drop schema public cascade;
create schema public;
`);
if ( fs.existsSync(ROOT_TMP_PATH) ) {
fse.removeSync(ROOT_TMP_PATH);
}
fs.mkdirSync(ROOT_TMP_PATH);
});
afterEach(async() => {
await db.end();
DDLManager.stopWatch();
});
it("watch nonexistent folder", async() => {
try {
await DDLManager.watch({
db,
folder: "---"
});
assert.ok(false, "expected error for nonexistent folder");
} catch(err) {
assert.equal(err.message, "folder \"---\" not found");
}
});
it("watch empty folder", async() => {
const folderPath = ROOT_TMP_PATH + "/empty";
fs.mkdirSync(folderPath);
await DDLManager.watch({
db,
folder: folderPath
});
// expected build without errors
assert.ok(true);
});
it("watch simple function", async() => {
const folderPath = ROOT_TMP_PATH + "/watch-func";
let result;
let row;
fs.mkdirSync(folderPath);
fs.writeFileSync(folderPath + "/watch_test.sql", `
create or replace function some_func()
returns integer as $body$
begin
return 1;
end
$body$
language plpgsql;
`);
await DDLManager.build({
db,
folder: folderPath
});
await DDLManager.watch({
db,
folder: folderPath
});
result = await db.query("select some_func() as some_func");
row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
some_func: 1
});
// change function
fs.writeFileSync(folderPath + "/watch_test.sql", `
create or replace function some_func()
returns integer as $body$
begin
return 2;
end
$body$
language plpgsql;
`);
await sleep(150);
result = await db.query("select some_func() as some_func");
row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
some_func: 2
});
});
it("watch with dbConfig", async() => {
const folderPath = ROOT_TMP_PATH + "/simple-func";
fs.mkdirSync(folderPath);
await DDLManager.watch({
db: {
database: dbConfig.database,
user: dbConfig.user,
password: dbConfig.password,
host: dbConfig.host,
port: dbConfig.port
},
folder: folderPath
});
fs.writeFileSync(folderPath + "/nice.sql", `
create or replace function nice()
returns integer as $body$
begin
return 1;
end
$body$
language plpgsql;
`);
await sleep(180);
const result = await db.query("select nice() as nice");
const row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
nice: 1
});
});
it("watch file with error, and without", async() => {
await DDLManager.watch({
db,
folder: ROOT_TMP_PATH
});
await DDLManager.watch({
db,
folder: ROOT_TMP_PATH
});
// file with syntax error
fs.writeFileSync(ROOT_TMP_PATH + "/test_errors.sql", `
create or replace
`);
await sleep(200);
// file without syntax error
fs.writeFileSync(ROOT_TMP_PATH + "/test_errors.sql", `
create or replace function test()
returns integer as $$select 1$$
language sql;
`);
await sleep(200);
const result = await db.query("select test() as nice");
const row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
nice: 1
});
});
it("drop frozen function, if she was replaced by ddl-manager", async() => {
let result;
let row;
await DDLManager.watch({
db,
folder: ROOT_TMP_PATH
});
// create frozen function
await db.query(`
create or replace function test()
returns integer as $body$
begin
return 1;
end
$body$
language plpgsql;
`);
// create function with same function identify
fs.writeFileSync(ROOT_TMP_PATH + "/test.sql", `
create or replace function test()
returns integer as $body$
begin
return 2;
end
$body$
language plpgsql;
`);
await sleep(150);
result = await db.query("select test() as test");
row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
test: 2
});
// change function name
fs.writeFileSync(ROOT_TMP_PATH + "/test.sql", `
create or replace function test2()
returns integer as $body$
begin
return 2;
end
$body$
language plpgsql;
`);
await sleep(150);
// new function must be created
result = await db.query("select test2() as test2");
row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
test2: 2
});
// frozen function should be dropped
try {
await db.query("select test() as test");
assert.ok(false, "function test() was not dropped");
} catch(err) {
assert.ok(true);
}
});
it("watch folder '/../some' ", async() => {
const folderPath = ROOT_TMP_PATH + "/watch";
let result;
let row;
fs.mkdirSync(folderPath);
fs.writeFileSync(folderPath + "/watch_test.sql", `
create or replace function some_func()
returns integer as $body$
begin
return 1;
end
$body$
language plpgsql;
`);
await DDLManager.build({
db,
folder: folderPath
});
await DDLManager.watch({
db,
folder: folderPath + "/../watch"
});
result = await db.query("select some_func() as some_func");
row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
some_func: 1
});
// change function
fs.writeFileSync(folderPath + "/watch_test.sql", `
create or replace function some_func()
returns integer as $body$
begin
return 2;
end
$body$
language plpgsql;
`);
await sleep(150);
result = await db.query("select some_func() as some_func");
row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
some_func: 2
});
// change function again, but by another path
fs.writeFileSync(folderPath + "/../watch/watch_test.sql", `
create or replace function some_func()
returns integer as $body$
begin
return 3;
end
$body$
language plpgsql;
`);
await sleep(150);
result = await db.query("select some_func() as some_func");
row = result.rows[0];
expect(row).to.be.shallowDeepEqual({
some_func: 3
});
});
it("watch from many folders", async() => {
const folderPath1 = ROOT_TMP_PATH + "/many-folder-1";
fs.mkdirSync(folderPath1);
const folderPath2 = ROOT_TMP_PATH + "/many-folder-2";
fs.mkdirSync(folderPath2);
fs.writeFileSync(folderPath1 + "/func1.sql", `
create or replace function func1()
returns text as $body$
begin
return 'func1';
end
$body$
language plpgsql;
`);
fs.writeFileSync(folderPath2 + "/func2.sql", `
create or replace function func2()
returns text as $body$
begin
return 'func2';
end
$body$
language plpgsql;
`);
await DDLManager.build({
db,
folder: [folderPath1, folderPath2]
});
await DDLManager.watch({
db,
folder: [
folderPath1,
folderPath2
]
});
let result = await db.query(`
select
func1() as func1,
func2() as func2
`);
expect(result.rows[0]).to.be.shallowDeepEqual({
func1: "func1",
func2: "func2"
});
fs.writeFileSync(folderPath1 + "/func1.sql", `
create or replace function func1()
returns text as $body$
begin
return 'changed func1';
end
$body$
language plpgsql;
`);
fs.writeFileSync(folderPath2 + "/func2.sql", `
create or replace function func2()
returns text as $body$
begin
return 'changed func2';
end
$body$
language plpgsql;
`);
await sleep(150);
result = await db.query(`
select
func1() as func1,
func2() as func2
`);
expect(result.rows[0]).to.be.shallowDeepEqual({
func1: "changed func1",
func2: "changed func2"
});
});
it("update cache columns in watcher mode (cpu high load)", async() => {
const folderPath = ROOT_TMP_PATH + "/watch-cache";
await db.query(`
create table companies (
id serial primary key
);
create table orders (
id serial primary key,
id_client integer
);
insert into companies default values;
insert into companies default values;
insert into orders (id_client) values (1);
insert into orders (id_client) values (1);
`);
fs.mkdirSync(folderPath);
await DDLManager.watch({
db,
folder: folderPath
});
fs.writeFileSync(folderPath + "/watch_cache.sql", `
cache totals for companies (
select count(*) as orders_count
from orders
where orders.id_client = companies.id
)
`);
await sleep(100);
const result = await db.query(`
select id, orders_count
from companies
order by id
`);
assert.deepStrictEqual(result.rows, [
{id: 1, orders_count: "2"},
{id: 2, orders_count: "0"}
]);
});
it("change cache triggers", async() => {
const folderPath = ROOT_TMP_PATH + "/watch-cache";
await db.query(`
create table companies (
id serial primary key
);
create table orders (
id serial primary key,
id_client integer,
profit numeric(14, 2)
);
insert into companies default values;
`);
fs.mkdirSync(folderPath);
fs.writeFileSync(folderPath + "/watch_cache.sql", `
cache totals for companies (
select
sum( orders.profit ) as orders_profit
from orders
where
orders.id_client = companies.id
)
`);
await DDLManager.build({
db,
folder: folderPath
});
await DDLManager.watch({
db,
folder: folderPath
});
let result;
// check first logic
await db.query(`
insert into orders (id_client, profit) values (1, 100);
`);
result = await db.query(`
select id, orders_profit
from companies
order by id
`);
assert.deepStrictEqual(result.rows, [
{id: 1, orders_profit: "100.00"}
]);
// clear cache
await db.query(`
delete from orders;
`);
await db.query(`
update companies set
orders_profit = 0;
`);
// change cache
fs.writeFileSync(folderPath + "/watch_cache.sql", `
cache totals for companies (
select
sum( orders.profit * 2 ) as orders_profit
from orders
where
orders.id_client = companies.id
)
`);
await sleep(300);
await db.query(`
insert into orders (id_client, profit) values (1, 100);
`);
result = await db.query(`
select id, orders_profit
from companies
order by id
`);
assert.deepStrictEqual(result.rows, [
{id: 1, orders_profit: "200.00"}
]);
});
});