@accounter/server 0.0.9-alpha-20251210171954-c9c0e7693ebe08d3643d9ee2c00c03606a53e334 → 0.0.9-alpha-20251210173721-a5bb944d9abd3f354daf897072b78bea4d8269c9
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/CHANGELOG.md +5 -21
- package/dist/server/src/__tests__/helpers/db-setup.d.ts +1 -0
- package/dist/server/src/__tests__/helpers/db-setup.js +2 -0
- package/dist/server/src/__tests__/helpers/db-setup.js.map +1 -1
- package/dist/server/src/__tests__/helpers/seed-helpers.business.test.js +31 -23
- package/dist/server/src/__tests__/helpers/seed-helpers.business.test.js.map +1 -1
- package/dist/server/src/__tests__/helpers/seed-helpers.concurrent.test.js +8 -8
- package/dist/server/src/__tests__/helpers/seed-helpers.concurrent.test.js.map +1 -1
- package/dist/server/src/__tests__/helpers/seed-helpers.financial-entity.test.js +50 -41
- package/dist/server/src/__tests__/helpers/seed-helpers.financial-entity.test.js.map +1 -1
- package/dist/server/src/__tests__/helpers/seed-helpers.tax-category.test.js +31 -23
- package/dist/server/src/__tests__/helpers/seed-helpers.tax-category.test.js.map +1 -1
- package/dist/server/src/__tests__/helpers/test-db-config.js +1 -1
- package/dist/server/src/__tests__/helpers/test-db-config.js.map +1 -1
- package/dist/server/src/__tests__/seed-admin-context.integration.test.js +131 -128
- package/dist/server/src/__tests__/seed-admin-context.integration.test.js.map +1 -1
- package/dist/server/src/modules/business-trips/providers/business-trips.provider.d.ts +1 -1
- package/dist/server/src/modules/business-trips/providers/business-trips.provider.js +1 -1
- package/dist/server/src/modules/business-trips/providers/business-trips.provider.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/helpers/db-setup.ts +3 -0
- package/src/__tests__/helpers/seed-helpers.business.test.ts +147 -145
- package/src/__tests__/helpers/seed-helpers.concurrent.test.ts +10 -10
- package/src/__tests__/helpers/seed-helpers.financial-entity.test.ts +231 -218
- package/src/__tests__/helpers/seed-helpers.tax-category.test.ts +164 -162
- package/src/__tests__/helpers/test-db-config.ts +1 -1
- package/src/__tests__/seed-admin-context.integration.test.ts +208 -199
- package/src/modules/business-trips/providers/business-trips.provider.ts +1 -1
|
@@ -1,174 +1,176 @@
|
|
|
1
|
-
import { describe, expect, it, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import
|
|
1
|
+
import { describe, expect, it, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import pg from 'pg';
|
|
3
|
+
import { testDbConfig, qualifyTable } from './test-db-config.js';
|
|
3
4
|
import { ensureFinancialEntity, ensureTaxCategoryForEntity } from './seed-helpers.js';
|
|
4
5
|
import { EntityValidationError } from './seed-errors.js';
|
|
5
|
-
import { TestDatabase } from './db-setup.js';
|
|
6
6
|
|
|
7
7
|
describe('ensureTaxCategoryForEntity', () => {
|
|
8
|
-
let
|
|
8
|
+
let pool: pg.Pool;
|
|
9
|
+
let client: pg.PoolClient;
|
|
9
10
|
|
|
10
11
|
beforeAll(async () => {
|
|
11
|
-
|
|
12
|
-
await
|
|
12
|
+
pool = new pg.Pool(testDbConfig);
|
|
13
|
+
client = await pool.connect();
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
afterAll(async () => {
|
|
16
|
-
|
|
17
|
+
client.release();
|
|
18
|
+
await pool.end();
|
|
17
19
|
});
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
await client.query('BEGIN');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
await client.query('ROLLBACK');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should create tax category on first call', async () => {
|
|
30
|
+
// Create financial entity
|
|
31
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
32
|
+
name: 'VAT Category',
|
|
33
|
+
type: 'tax_category',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Create tax category
|
|
37
|
+
await ensureTaxCategoryForEntity(client, entityId);
|
|
38
|
+
|
|
39
|
+
// Verify tax category exists
|
|
40
|
+
const result = await client.query(
|
|
41
|
+
`SELECT id FROM ${qualifyTable('tax_categories')} WHERE id = $1`,
|
|
42
|
+
[entityId],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(result.rows).toHaveLength(1);
|
|
46
|
+
expect(result.rows[0].id).toBe(entityId);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should be idempotent (no-op on repeated calls)', async () => {
|
|
50
|
+
// Create financial entity
|
|
51
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
52
|
+
name: 'Income Tax',
|
|
53
|
+
type: 'tax_category',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Create tax category twice
|
|
57
|
+
await ensureTaxCategoryForEntity(client, entityId);
|
|
58
|
+
await ensureTaxCategoryForEntity(client, entityId);
|
|
59
|
+
|
|
60
|
+
// Verify only one row exists
|
|
61
|
+
const result = await client.query(
|
|
62
|
+
`SELECT COUNT(*) as count FROM ${qualifyTable('tax_categories')} WHERE id = $1`,
|
|
63
|
+
[entityId],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(result.rows[0].count).toBe('1');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should preserve existing values on subsequent calls', async () => {
|
|
70
|
+
// Create financial entity
|
|
71
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
72
|
+
name: 'Expense Category',
|
|
73
|
+
type: 'tax_category',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Create tax category
|
|
77
|
+
await ensureTaxCategoryForEntity(client, entityId);
|
|
78
|
+
|
|
79
|
+
// Get initial state
|
|
80
|
+
const initialResult = await client.query(
|
|
81
|
+
`SELECT * FROM ${qualifyTable('tax_categories')} WHERE id = $1`,
|
|
82
|
+
[entityId],
|
|
83
|
+
);
|
|
84
|
+
const initialRow = initialResult.rows[0];
|
|
85
|
+
|
|
86
|
+
// Call again (should not modify)
|
|
87
|
+
await ensureTaxCategoryForEntity(client, entityId, { sortCode: 999 });
|
|
88
|
+
|
|
89
|
+
// Verify unchanged
|
|
90
|
+
const finalResult = await client.query(
|
|
91
|
+
`SELECT * FROM ${qualifyTable('tax_categories')} WHERE id = $1`,
|
|
92
|
+
[entityId],
|
|
93
|
+
);
|
|
94
|
+
const finalRow = finalResult.rows[0];
|
|
95
|
+
|
|
96
|
+
expect(finalRow).toEqual(initialRow);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should reject invalid UUID format', async () => {
|
|
100
|
+
await expect(
|
|
101
|
+
ensureTaxCategoryForEntity(client, 'not-a-uuid'),
|
|
102
|
+
).rejects.toThrow(EntityValidationError);
|
|
103
|
+
|
|
104
|
+
await expect(
|
|
105
|
+
ensureTaxCategoryForEntity(client, ''),
|
|
106
|
+
).rejects.toThrow(EntityValidationError);
|
|
107
|
+
|
|
108
|
+
await expect(
|
|
109
|
+
ensureTaxCategoryForEntity(client, '12345'),
|
|
110
|
+
).rejects.toThrow(EntityValidationError);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should reject non-existent financial entity', async () => {
|
|
114
|
+
const fakeUuid = '00000000-0000-0000-0000-000000000001';
|
|
115
|
+
|
|
116
|
+
await expect(
|
|
117
|
+
ensureTaxCategoryForEntity(client, fakeUuid),
|
|
118
|
+
).rejects.toThrow(EntityValidationError);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should not leak data between tests', async () => {
|
|
122
|
+
// This test verifies transactional isolation by checking that data
|
|
123
|
+
// from previous tests is not visible
|
|
124
|
+
const result = await client.query(
|
|
125
|
+
`SELECT COUNT(*) as count FROM ${qualifyTable('tax_categories')} WHERE id IN (SELECT id FROM ${qualifyTable('financial_entities')} WHERE name LIKE 'VAT Category' OR name LIKE 'Income Tax' OR name LIKE 'Expense Category')`,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Due to ROLLBACK after each test, count should be 0
|
|
129
|
+
expect(parseInt(result.rows[0].count)).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should work with entities that have owner_id', async () => {
|
|
133
|
+
// Create owner business entity (without owner_id requirement)
|
|
134
|
+
const { id: _ownerId } = await ensureFinancialEntity(client, {
|
|
135
|
+
name: 'Owner Business',
|
|
136
|
+
type: 'business',
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Create owned tax category entity (tax categories can have owner_id)
|
|
140
|
+
const { id: taxCatId } = await ensureFinancialEntity(client, {
|
|
141
|
+
name: 'Owned Tax Category',
|
|
142
|
+
type: 'tax_category',
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Create tax category
|
|
146
|
+
await ensureTaxCategoryForEntity(client, taxCatId);
|
|
147
|
+
|
|
148
|
+
// Verify tax category exists
|
|
149
|
+
const result = await client.query(
|
|
150
|
+
`SELECT id FROM ${qualifyTable('tax_categories')} WHERE id = $1`,
|
|
151
|
+
[taxCatId],
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
expect(result.rows).toHaveLength(1);
|
|
155
|
+
expect(result.rows[0].id).toBe(taxCatId);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should handle options parameter gracefully (future-proofing)', async () => {
|
|
159
|
+
// Create financial entity
|
|
160
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
161
|
+
name: 'Category with Options',
|
|
162
|
+
type: 'tax_category',
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Create tax category with options
|
|
166
|
+
await ensureTaxCategoryForEntity(client, entityId, { sortCode: 1000 });
|
|
167
|
+
|
|
168
|
+
// Verify tax category exists
|
|
169
|
+
const result = await client.query(
|
|
170
|
+
`SELECT id FROM ${qualifyTable('tax_categories')} WHERE id = $1`,
|
|
171
|
+
[entityId],
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
expect(result.rows).toHaveLength(1);
|
|
175
|
+
});
|
|
174
176
|
});
|
|
@@ -13,7 +13,7 @@ export const testDbConfig: PoolConfig = {
|
|
|
13
13
|
password: process.env.POSTGRES_PASSWORD || 'postgres',
|
|
14
14
|
host: process.env.POSTGRES_HOST || 'localhost',
|
|
15
15
|
port: parseInt(process.env.POSTGRES_PORT || '5432', 10),
|
|
16
|
-
database: process.env.POSTGRES_DB || '
|
|
16
|
+
database: process.env.POSTGRES_DB || 'accounter',
|
|
17
17
|
ssl: process.env.POSTGRES_SSL === '1',
|
|
18
18
|
};
|
|
19
19
|
|