Skip to content

Commit 4e89912

Browse files
georgRusanovgeorgiy.rusanov
andauthored
chore: added tests (#979)
* chore: added tests * chore: added more tests * chore: try to fix test on ci --------- Co-authored-by: georgiy.rusanov <[email protected]>
1 parent 28cd3e6 commit 4e89912

14 files changed

+1432
-1
lines changed

test/admin-app.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { expect, test, describe } from 'vitest'
2+
import { build } from '../src/server/admin-app.js'
3+
4+
describe('admin-app', () => {
5+
test('should register metrics endpoint', async () => {
6+
const app = build()
7+
8+
// Test that the app can be started (this will trigger plugin registration)
9+
await app.ready()
10+
11+
// Verify that metrics endpoint is available
12+
const routes = app.printRoutes()
13+
expect(routes).toContain('metrics')
14+
15+
await app.close()
16+
})
17+
})

test/app.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { expect, test, describe } from 'vitest'
2+
import { build } from '../src/server/app.js'
3+
4+
describe('server/app', () => {
5+
test('should handle root endpoint', async () => {
6+
const app = build()
7+
const response = await app.inject({
8+
method: 'GET',
9+
url: '/',
10+
})
11+
expect(response.statusCode).toBe(200)
12+
const data = JSON.parse(response.body)
13+
expect(data).toHaveProperty('status')
14+
expect(data).toHaveProperty('name')
15+
expect(data).toHaveProperty('version')
16+
expect(data).toHaveProperty('documentation')
17+
await app.close()
18+
})
19+
20+
test('should handle health endpoint', async () => {
21+
const app = build()
22+
const response = await app.inject({
23+
method: 'GET',
24+
url: '/health',
25+
})
26+
expect(response.statusCode).toBe(200)
27+
const data = JSON.parse(response.body)
28+
expect(data).toHaveProperty('date')
29+
await app.close()
30+
})
31+
})

test/config.test.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { expect, test, describe } from 'vitest'
2+
import { build } from '../src/server/app.js'
3+
import { TEST_CONNECTION_STRING } from './lib/utils.js'
4+
5+
describe('server/routes/config', () => {
6+
test('should list config with query parameters', async () => {
7+
const app = build()
8+
const response = await app.inject({
9+
method: 'GET',
10+
url: '/config?limit=5&offset=0',
11+
headers: {
12+
pg: TEST_CONNECTION_STRING,
13+
},
14+
})
15+
expect(response.statusCode).toBe(200)
16+
expect(response.json()).toMatchInlineSnapshot(`
17+
[
18+
{
19+
"boot_val": "on",
20+
"category": "Autovacuum",
21+
"context": "sighup",
22+
"enumvals": null,
23+
"extra_desc": null,
24+
"group": "Autovacuum",
25+
"max_val": null,
26+
"min_val": null,
27+
"name": "autovacuum",
28+
"pending_restart": false,
29+
"reset_val": "on",
30+
"setting": "on",
31+
"short_desc": "Starts the autovacuum subprocess.",
32+
"source": "default",
33+
"sourcefile": null,
34+
"sourceline": null,
35+
"subgroup": "",
36+
"unit": null,
37+
"vartype": "bool",
38+
},
39+
{
40+
"boot_val": "0.1",
41+
"category": "Autovacuum",
42+
"context": "sighup",
43+
"enumvals": null,
44+
"extra_desc": null,
45+
"group": "Autovacuum",
46+
"max_val": "100",
47+
"min_val": "0",
48+
"name": "autovacuum_analyze_scale_factor",
49+
"pending_restart": false,
50+
"reset_val": "0.1",
51+
"setting": "0.1",
52+
"short_desc": "Number of tuple inserts, updates, or deletes prior to analyze as a fraction of reltuples.",
53+
"source": "default",
54+
"sourcefile": null,
55+
"sourceline": null,
56+
"subgroup": "",
57+
"unit": null,
58+
"vartype": "real",
59+
},
60+
{
61+
"boot_val": "50",
62+
"category": "Autovacuum",
63+
"context": "sighup",
64+
"enumvals": null,
65+
"extra_desc": null,
66+
"group": "Autovacuum",
67+
"max_val": "2147483647",
68+
"min_val": "0",
69+
"name": "autovacuum_analyze_threshold",
70+
"pending_restart": false,
71+
"reset_val": "50",
72+
"setting": "50",
73+
"short_desc": "Minimum number of tuple inserts, updates, or deletes prior to analyze.",
74+
"source": "default",
75+
"sourcefile": null,
76+
"sourceline": null,
77+
"subgroup": "",
78+
"unit": null,
79+
"vartype": "integer",
80+
},
81+
{
82+
"boot_val": "200000000",
83+
"category": "Autovacuum",
84+
"context": "postmaster",
85+
"enumvals": null,
86+
"extra_desc": null,
87+
"group": "Autovacuum",
88+
"max_val": "2000000000",
89+
"min_val": "100000",
90+
"name": "autovacuum_freeze_max_age",
91+
"pending_restart": false,
92+
"reset_val": "200000000",
93+
"setting": "200000000",
94+
"short_desc": "Age at which to autovacuum a table to prevent transaction ID wraparound.",
95+
"source": "default",
96+
"sourcefile": null,
97+
"sourceline": null,
98+
"subgroup": "",
99+
"unit": null,
100+
"vartype": "integer",
101+
},
102+
{
103+
"boot_val": "3",
104+
"category": "Autovacuum",
105+
"context": "postmaster",
106+
"enumvals": null,
107+
"extra_desc": null,
108+
"group": "Autovacuum",
109+
"max_val": "262143",
110+
"min_val": "1",
111+
"name": "autovacuum_max_workers",
112+
"pending_restart": false,
113+
"reset_val": "3",
114+
"setting": "3",
115+
"short_desc": "Sets the maximum number of simultaneously running autovacuum worker processes.",
116+
"source": "default",
117+
"sourcefile": null,
118+
"sourceline": null,
119+
"subgroup": "",
120+
"unit": null,
121+
"vartype": "integer",
122+
},
123+
]
124+
`)
125+
await app.close()
126+
})
127+
})

test/extensions.test.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { expect, test, describe } from 'vitest'
2+
import { build } from '../src/server/app.js'
3+
import { TEST_CONNECTION_STRING } from './lib/utils.js'
4+
5+
describe('server/routes/extensions', () => {
6+
test('should list extensions', async () => {
7+
const app = build()
8+
const response = await app.inject({
9+
method: 'GET',
10+
url: '/extensions',
11+
headers: {
12+
pg: TEST_CONNECTION_STRING,
13+
},
14+
})
15+
expect(response.statusCode).toBe(200)
16+
expect(Array.isArray(JSON.parse(response.body))).toBe(true)
17+
await app.close()
18+
})
19+
20+
test('should list extensions with query parameters', async () => {
21+
const app = build()
22+
const response = await app.inject({
23+
method: 'GET',
24+
url: '/extensions?limit=5&offset=0',
25+
headers: {
26+
pg: TEST_CONNECTION_STRING,
27+
},
28+
})
29+
expect(response.statusCode).toBe(200)
30+
expect(Array.isArray(JSON.parse(response.body))).toBe(true)
31+
await app.close()
32+
})
33+
34+
test('should return 404 for non-existent extension', async () => {
35+
const app = build()
36+
const response = await app.inject({
37+
method: 'GET',
38+
url: '/extensions/non-existent-extension',
39+
headers: {
40+
pg: TEST_CONNECTION_STRING,
41+
},
42+
})
43+
expect(response.statusCode).toBe(404)
44+
await app.close()
45+
})
46+
47+
test('should create extension, retrieve, update, delete', async () => {
48+
const app = build()
49+
const response = await app.inject({
50+
method: 'POST',
51+
url: '/extensions',
52+
headers: {
53+
pg: TEST_CONNECTION_STRING,
54+
},
55+
payload: { name: 'pgcrypto', version: '1.3' },
56+
})
57+
expect(response.statusCode).toBe(200)
58+
expect(response.json()).toMatchInlineSnapshot(`
59+
{
60+
"comment": "cryptographic functions",
61+
"default_version": "1.3",
62+
"installed_version": "1.3",
63+
"name": "pgcrypto",
64+
"schema": "public",
65+
}
66+
`)
67+
68+
const retrieveResponse = await app.inject({
69+
method: 'GET',
70+
url: '/extensions/pgcrypto',
71+
headers: {
72+
pg: TEST_CONNECTION_STRING,
73+
},
74+
})
75+
expect(retrieveResponse.statusCode).toBe(200)
76+
expect(retrieveResponse.json()).toMatchInlineSnapshot(`
77+
{
78+
"comment": "cryptographic functions",
79+
"default_version": "1.3",
80+
"installed_version": "1.3",
81+
"name": "pgcrypto",
82+
"schema": "public",
83+
}
84+
`)
85+
86+
const updateResponse = await app.inject({
87+
method: 'PATCH',
88+
url: '/extensions/pgcrypto',
89+
headers: {
90+
pg: TEST_CONNECTION_STRING,
91+
},
92+
payload: { schema: 'public' },
93+
})
94+
expect(updateResponse.statusCode).toBe(200)
95+
expect(updateResponse.json()).toMatchInlineSnapshot(`
96+
{
97+
"comment": "cryptographic functions",
98+
"default_version": "1.3",
99+
"installed_version": "1.3",
100+
"name": "pgcrypto",
101+
"schema": "public",
102+
}
103+
`)
104+
105+
const deleteResponse = await app.inject({
106+
method: 'DELETE',
107+
url: '/extensions/pgcrypto',
108+
headers: {
109+
pg: TEST_CONNECTION_STRING,
110+
},
111+
})
112+
expect(deleteResponse.statusCode).toBe(200)
113+
expect(deleteResponse.json()).toMatchInlineSnapshot(`
114+
{
115+
"comment": "cryptographic functions",
116+
"default_version": "1.3",
117+
"installed_version": "1.3",
118+
"name": "pgcrypto",
119+
"schema": "public",
120+
}
121+
`)
122+
123+
await app.close()
124+
})
125+
126+
test('should return 400 for invalid extension name', async () => {
127+
const app = build()
128+
const response = await app.inject({
129+
method: 'POST',
130+
url: '/extensions',
131+
headers: {
132+
pg: TEST_CONNECTION_STRING,
133+
},
134+
payload: { name: 'invalid-extension', version: '1.3' },
135+
})
136+
expect(response.statusCode).toBe(400)
137+
expect(response.json()).toMatchInlineSnapshot(`
138+
{
139+
"error": "could not open extension control file "/usr/share/postgresql/14/extension/invalid-extension.control": No such file or directory",
140+
}
141+
`)
142+
await app.close()
143+
})
144+
})

0 commit comments

Comments
 (0)