@accounter/server 0.0.9-alpha-20251210173721-a5bb944d9abd3f354daf897072b78bea4d8269c9 → 0.0.9-alpha-20251210180846-69e726b23ba814acaf7af8af75df20475e55a7d7

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.
Files changed (28) hide show
  1. package/CHANGELOG.md +21 -5
  2. package/dist/server/src/__tests__/helpers/db-setup.d.ts +0 -1
  3. package/dist/server/src/__tests__/helpers/db-setup.js +0 -2
  4. package/dist/server/src/__tests__/helpers/db-setup.js.map +1 -1
  5. package/dist/server/src/__tests__/helpers/seed-helpers.business.test.js +23 -31
  6. package/dist/server/src/__tests__/helpers/seed-helpers.business.test.js.map +1 -1
  7. package/dist/server/src/__tests__/helpers/seed-helpers.concurrent.test.js +8 -8
  8. package/dist/server/src/__tests__/helpers/seed-helpers.concurrent.test.js.map +1 -1
  9. package/dist/server/src/__tests__/helpers/seed-helpers.financial-entity.test.js +41 -50
  10. package/dist/server/src/__tests__/helpers/seed-helpers.financial-entity.test.js.map +1 -1
  11. package/dist/server/src/__tests__/helpers/seed-helpers.tax-category.test.js +23 -31
  12. package/dist/server/src/__tests__/helpers/seed-helpers.tax-category.test.js.map +1 -1
  13. package/dist/server/src/__tests__/helpers/test-db-config.js +1 -1
  14. package/dist/server/src/__tests__/helpers/test-db-config.js.map +1 -1
  15. package/dist/server/src/__tests__/seed-admin-context.integration.test.js +128 -131
  16. package/dist/server/src/__tests__/seed-admin-context.integration.test.js.map +1 -1
  17. package/dist/server/src/modules/business-trips/providers/business-trips.provider.d.ts +1 -1
  18. package/dist/server/src/modules/business-trips/providers/business-trips.provider.js +1 -1
  19. package/dist/server/src/modules/business-trips/providers/business-trips.provider.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/__tests__/helpers/db-setup.ts +0 -3
  22. package/src/__tests__/helpers/seed-helpers.business.test.ts +145 -147
  23. package/src/__tests__/helpers/seed-helpers.concurrent.test.ts +10 -10
  24. package/src/__tests__/helpers/seed-helpers.financial-entity.test.ts +218 -231
  25. package/src/__tests__/helpers/seed-helpers.tax-category.test.ts +162 -164
  26. package/src/__tests__/helpers/test-db-config.ts +1 -1
  27. package/src/__tests__/seed-admin-context.integration.test.ts +199 -208
  28. package/src/modules/business-trips/providers/business-trips.provider.ts +1 -1
@@ -1,258 +1,245 @@
1
- import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
2
- import pg from 'pg';
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
3
2
  import { ensureFinancialEntity } from './seed-helpers.js';
4
- import { testDbConfig, qualifyTable } from './test-db-config.js';
3
+ import { qualifyTable } from './test-db-config.js';
5
4
  import { EntityValidationError } from './seed-errors.js';
5
+ import { TestDatabase } from './db-setup.js';
6
6
 
7
7
  describe('ensureFinancialEntity', () => {
8
- let pool: pg.Pool;
9
- let client: pg.PoolClient;
8
+ let db: TestDatabase;
10
9
 
11
10
  beforeAll(async () => {
12
- // Create connection pool with shared config
13
- pool = new pg.Pool(testDbConfig);
14
-
15
- // Get a client for transactions
16
- client = await pool.connect();
11
+ db = new TestDatabase();
12
+ await db.connect();
17
13
  });
18
14
 
19
15
  afterAll(async () => {
20
- // Release client and close pool
21
- if (client) {
22
- client.release();
23
- }
24
- if (pool) {
25
- await pool.end();
26
- }
27
- });
28
-
29
- beforeEach(async () => {
30
- // Start transaction before each test
31
- await client.query('BEGIN');
32
- });
33
-
34
- afterEach(async () => {
35
- // Rollback transaction after each test to clean up
36
- await client.query('ROLLBACK');
16
+ await db.close();
37
17
  });
38
18
 
39
- it('should create a new financial entity when it does not exist', async () => {
40
- const result = await ensureFinancialEntity(client, {
41
- name: 'Test Entity',
42
- type: 'business',
43
- });
44
-
45
- expect(result).toHaveProperty('id');
46
- expect(typeof result.id).toBe('string');
47
-
48
- // Verify it was inserted
49
- const checkResult = await client.query(
50
- `SELECT id, name, type, owner_id FROM ${qualifyTable('financial_entities')} WHERE id = $1`,
51
- [result.id],
52
- );
53
-
54
- expect(checkResult.rows.length).toBe(1);
55
- expect(checkResult.rows[0].name).toBe('Test Entity');
56
- expect(checkResult.rows[0].type).toBe('business');
57
- expect(checkResult.rows[0].owner_id).toBeNull();
58
- });
59
-
60
- it('should return existing entity when called twice with same parameters', async () => {
61
- const firstResult = await ensureFinancialEntity(client, {
62
- name: 'Duplicate Test',
63
- type: 'tax_category',
64
- });
65
-
66
- const secondResult = await ensureFinancialEntity(client, {
67
- name: 'Duplicate Test',
68
- type: 'tax_category',
69
- });
70
-
71
- expect(firstResult.id).toBe(secondResult.id);
72
-
73
- // Verify only one row exists
74
- const countResult = await client.query(
75
- `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')} WHERE name = $1 AND type = $2`,
76
- ['Duplicate Test', 'tax_category'],
77
- );
78
-
79
- expect(parseInt(countResult.rows[0].count)).toBe(1);
80
- });
81
-
82
- it('should handle entities with owner_id', async () => {
83
- // First create an owner entity (business)
84
- const ownerEntityResult = await ensureFinancialEntity(client, {
85
- name: 'Owner Entity',
86
- type: 'business',
87
- });
88
-
89
- // Create corresponding business record (required for foreign key)
90
- await client.query(
91
- `INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`,
92
- [ownerEntityResult.id],
93
- );
94
-
95
- // Create owned entity (tax_category)
96
- const result = await ensureFinancialEntity(client, {
97
- name: 'Owned Entity',
98
- type: 'tax_category',
99
- ownerId: ownerEntityResult.id,
100
- });
101
-
102
- expect(result).toHaveProperty('id');
103
-
104
- // Verify owner_id is set correctly
105
- const checkResult = await client.query(
106
- `SELECT owner_id FROM ${qualifyTable('financial_entities')} WHERE id = $1`,
107
- [result.id],
108
- );
109
-
110
- expect(checkResult.rows[0].owner_id).toBe(ownerEntityResult.id);
111
- });
112
-
113
- it('should be idempotent for entities with owner_id', async () => {
114
- const ownerEntityResult = await ensureFinancialEntity(client, {
115
- name: 'Owner Business',
116
- type: 'business',
117
- });
118
-
119
- // Create corresponding business record
120
- await client.query(
121
- `INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`,
122
- [ownerEntityResult.id],
123
- );
124
-
125
- const firstResult = await ensureFinancialEntity(client, {
126
- name: 'Child Entity',
127
- type: 'tax_category',
128
- ownerId: ownerEntityResult.id,
129
- });
130
-
131
- const secondResult = await ensureFinancialEntity(client, {
132
- name: 'Child Entity',
133
- type: 'tax_category',
134
- ownerId: ownerEntityResult.id,
135
- });
136
-
137
- expect(firstResult.id).toBe(secondResult.id);
138
-
139
- // Verify only one row
140
- const countResult = await client.query(
141
- `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')}
19
+ it('should create a new financial entity when it does not exist', async () =>
20
+ db.withTransaction(async client => {
21
+ const result = await ensureFinancialEntity(client, {
22
+ name: 'Test Entity',
23
+ type: 'business',
24
+ });
25
+
26
+ expect(result).toHaveProperty('id');
27
+ expect(typeof result.id).toBe('string');
28
+
29
+ // Verify it was inserted
30
+ const checkResult = await client.query(
31
+ `SELECT id, name, type, owner_id FROM ${qualifyTable('financial_entities')} WHERE id = $1`,
32
+ [result.id],
33
+ );
34
+
35
+ expect(checkResult.rows.length).toBe(1);
36
+ expect(checkResult.rows[0].name).toBe('Test Entity');
37
+ expect(checkResult.rows[0].type).toBe('business');
38
+ expect(checkResult.rows[0].owner_id).toBeNull();
39
+ }));
40
+
41
+ it('should return existing entity when called twice with same parameters', async () =>
42
+ db.withTransaction(async client => {
43
+ const firstResult = await ensureFinancialEntity(client, {
44
+ name: 'Duplicate Test',
45
+ type: 'tax_category',
46
+ });
47
+
48
+ const secondResult = await ensureFinancialEntity(client, {
49
+ name: 'Duplicate Test',
50
+ type: 'tax_category',
51
+ });
52
+
53
+ expect(firstResult.id).toBe(secondResult.id);
54
+
55
+ // Verify only one row exists
56
+ const countResult = await client.query(
57
+ `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')} WHERE name = $1 AND type = $2`,
58
+ ['Duplicate Test', 'tax_category'],
59
+ );
60
+
61
+ expect(parseInt(countResult.rows[0].count)).toBe(1);
62
+ }));
63
+
64
+ it('should handle entities with owner_id', async () =>
65
+ db.withTransaction(async client => {
66
+ // First create an owner entity (business)
67
+ const ownerEntityResult = await ensureFinancialEntity(client, {
68
+ name: 'Owner Entity',
69
+ type: 'business',
70
+ });
71
+
72
+ // Create corresponding business record (required for foreign key)
73
+ await client.query(`INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`, [
74
+ ownerEntityResult.id,
75
+ ]);
76
+
77
+ // Create owned entity (tax_category)
78
+ const result = await ensureFinancialEntity(client, {
79
+ name: 'Owned Entity',
80
+ type: 'tax_category',
81
+ ownerId: ownerEntityResult.id,
82
+ });
83
+
84
+ expect(result).toHaveProperty('id');
85
+
86
+ // Verify owner_id is set correctly
87
+ const checkResult = await client.query(
88
+ `SELECT owner_id FROM ${qualifyTable('financial_entities')} WHERE id = $1`,
89
+ [result.id],
90
+ );
91
+
92
+ expect(checkResult.rows[0].owner_id).toBe(ownerEntityResult.id);
93
+ }));
94
+
95
+ it('should be idempotent for entities with owner_id', async () =>
96
+ db.withTransaction(async client => {
97
+ const ownerEntityResult = await ensureFinancialEntity(client, {
98
+ name: 'Owner Business',
99
+ type: 'business',
100
+ });
101
+
102
+ // Create corresponding business record
103
+ await client.query(`INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`, [
104
+ ownerEntityResult.id,
105
+ ]);
106
+
107
+ const firstResult = await ensureFinancialEntity(client, {
108
+ name: 'Child Entity',
109
+ type: 'tax_category',
110
+ ownerId: ownerEntityResult.id,
111
+ });
112
+
113
+ const secondResult = await ensureFinancialEntity(client, {
114
+ name: 'Child Entity',
115
+ type: 'tax_category',
116
+ ownerId: ownerEntityResult.id,
117
+ });
118
+
119
+ expect(firstResult.id).toBe(secondResult.id);
120
+
121
+ // Verify only one row
122
+ const countResult = await client.query(
123
+ `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')}
142
124
  WHERE name = $1 AND type = $2 AND owner_id = $3`,
143
- ['Child Entity', 'tax_category', ownerEntityResult.id],
144
- );
125
+ ['Child Entity', 'tax_category', ownerEntityResult.id],
126
+ );
145
127
 
146
- expect(parseInt(countResult.rows[0].count)).toBe(1);
147
- });
128
+ expect(parseInt(countResult.rows[0].count)).toBe(1);
129
+ }));
148
130
 
149
- it('should distinguish entities by type', async () => {
150
- const businessResult = await ensureFinancialEntity(client, {
151
- name: 'Same Name',
152
- type: 'business',
153
- });
131
+ it('should distinguish entities by type', async () =>
132
+ db.withTransaction(async client => {
133
+ const businessResult = await ensureFinancialEntity(client, {
134
+ name: 'Same Name',
135
+ type: 'business',
136
+ });
154
137
 
155
- const taxCategoryResult = await ensureFinancialEntity(client, {
156
- name: 'Same Name',
157
- type: 'tax_category',
158
- });
138
+ const taxCategoryResult = await ensureFinancialEntity(client, {
139
+ name: 'Same Name',
140
+ type: 'tax_category',
141
+ });
159
142
 
160
- expect(businessResult.id).not.toBe(taxCategoryResult.id);
161
- });
143
+ expect(businessResult.id).not.toBe(taxCategoryResult.id);
144
+ }));
162
145
 
163
- it('should distinguish entities by owner_id', async () => {
164
- const owner1Entity = await ensureFinancialEntity(client, {
165
- name: 'Owner 1',
166
- type: 'business',
167
- });
168
- await client.query(
169
- `INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`,
170
- [owner1Entity.id],
171
- );
172
-
173
- const owner2Entity = await ensureFinancialEntity(client, {
174
- name: 'Owner 2',
175
- type: 'business',
176
- });
177
- await client.query(
178
- `INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`,
179
- [owner2Entity.id],
180
- );
181
-
182
- const entity1 = await ensureFinancialEntity(client, {
183
- name: 'Same Child',
184
- type: 'tax_category',
185
- ownerId: owner1Entity.id,
186
- });
187
-
188
- const entity2 = await ensureFinancialEntity(client, {
189
- name: 'Same Child',
190
- type: 'tax_category',
191
- ownerId: owner2Entity.id,
192
- });
193
-
194
- expect(entity1.id).not.toBe(entity2.id);
195
- });
146
+ it('should distinguish entities by owner_id', async () =>
147
+ db.withTransaction(async client => {
148
+ const owner1Entity = await ensureFinancialEntity(client, {
149
+ name: 'Owner 1',
150
+ type: 'business',
151
+ });
152
+ await client.query(`INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`, [
153
+ owner1Entity.id,
154
+ ]);
196
155
 
197
- it('should handle null vs undefined owner_id consistently', async () => {
198
- const result1 = await ensureFinancialEntity(client, {
199
- name: 'No Owner Entity',
200
- type: 'business',
201
- ownerId: undefined,
202
- });
156
+ const owner2Entity = await ensureFinancialEntity(client, {
157
+ name: 'Owner 2',
158
+ type: 'business',
159
+ });
160
+ await client.query(`INSERT INTO ${qualifyTable('businesses')} (id) VALUES ($1)`, [
161
+ owner2Entity.id,
162
+ ]);
163
+
164
+ const entity1 = await ensureFinancialEntity(client, {
165
+ name: 'Same Child',
166
+ type: 'tax_category',
167
+ ownerId: owner1Entity.id,
168
+ });
169
+
170
+ const entity2 = await ensureFinancialEntity(client, {
171
+ name: 'Same Child',
172
+ type: 'tax_category',
173
+ ownerId: owner2Entity.id,
174
+ });
175
+
176
+ expect(entity1.id).not.toBe(entity2.id);
177
+ }));
178
+
179
+ it('should handle null vs undefined owner_id consistently', async () =>
180
+ db.withTransaction(async client => {
181
+ const result1 = await ensureFinancialEntity(client, {
182
+ name: 'No Owner Entity',
183
+ type: 'business',
184
+ ownerId: undefined,
185
+ });
203
186
 
204
- const result2 = await ensureFinancialEntity(client, {
205
- name: 'No Owner Entity',
206
- type: 'business',
207
- });
187
+ const result2 = await ensureFinancialEntity(client, {
188
+ name: 'No Owner Entity',
189
+ type: 'business',
190
+ });
208
191
 
209
- expect(result1.id).toBe(result2.id);
210
- });
192
+ expect(result1.id).toBe(result2.id);
193
+ }));
211
194
 
212
- it('should not leak data between tests', async () => {
213
- // This test verifies that ROLLBACK works correctly
214
- // Create an entity in this test
215
- await ensureFinancialEntity(client, {
216
- name: 'Transient Entity',
217
- type: 'business',
218
- });
195
+ it('should not leak data between tests', async () =>
196
+ db.withTransaction(async client => {
197
+ // This test verifies that ROLLBACK works correctly
198
+ // Create an entity in this test
199
+ await ensureFinancialEntity(client, {
200
+ name: 'Transient Entity',
201
+ type: 'business',
202
+ });
219
203
 
220
- // The entity should exist within this transaction
221
- const checkInTransaction = await client.query(
222
- `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')} WHERE name = $1`,
223
- ['Transient Entity'],
224
- );
204
+ // The entity should exist within this transaction
205
+ const checkInTransaction = await client.query(
206
+ `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')} WHERE name = $1`,
207
+ ['Transient Entity'],
208
+ );
225
209
 
226
- expect(parseInt(checkInTransaction.rows[0].count)).toBe(1);
210
+ expect(parseInt(checkInTransaction.rows[0].count)).toBe(1);
227
211
 
228
- // After ROLLBACK in afterEach, this data won't be visible in next test
229
- });
212
+ // After ROLLBACK in afterEach, this data won't be visible in next test
213
+ }));
230
214
 
231
215
  // Validation tests
232
- it('should reject empty entity name', async () => {
233
- await expect(
234
- ensureFinancialEntity(client, {
235
- name: '',
236
- type: 'business',
237
- }),
238
- ).rejects.toThrow(EntityValidationError);
239
- });
240
-
241
- it('should reject invalid entity type', async () => {
242
- await expect(
243
- ensureFinancialEntity(client, {
244
- name: 'Test Entity',
245
- type: 'invalid_type' as any,
246
- }),
247
- ).rejects.toThrow(EntityValidationError);
248
- });
249
-
250
- it('should reject whitespace-only name', async () => {
251
- await expect(
252
- ensureFinancialEntity(client, {
253
- name: ' ',
254
- type: 'business',
255
- }),
256
- ).rejects.toThrow(EntityValidationError);
257
- });
216
+ it('should reject empty entity name', async () =>
217
+ db.withTransaction(async client => {
218
+ await expect(
219
+ ensureFinancialEntity(client, {
220
+ name: '',
221
+ type: 'business',
222
+ }),
223
+ ).rejects.toThrow(EntityValidationError);
224
+ }));
225
+
226
+ it('should reject invalid entity type', async () =>
227
+ db.withTransaction(async client => {
228
+ await expect(
229
+ ensureFinancialEntity(client, {
230
+ name: 'Test Entity',
231
+ type: 'invalid_type' as any,
232
+ }),
233
+ ).rejects.toThrow(EntityValidationError);
234
+ }));
235
+
236
+ it('should reject whitespace-only name', async () =>
237
+ db.withTransaction(async client => {
238
+ await expect(
239
+ ensureFinancialEntity(client, {
240
+ name: ' ',
241
+ type: 'business',
242
+ }),
243
+ ).rejects.toThrow(EntityValidationError);
244
+ }));
258
245
  });