@accounter/server 0.0.9-alpha-20251210173458-9f3d75bb8729ec6038c777f7dbf9a1b5fa727888 → 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,159 +1,161 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import pg from 'pg';
|
|
2
3
|
import { ensureFinancialEntity, ensureBusinessForEntity } from './seed-helpers.js';
|
|
3
|
-
import { qualifyTable } from './test-db-config.js';
|
|
4
|
+
import { testDbConfig, qualifyTable } from './test-db-config.js';
|
|
4
5
|
import { EntityValidationError } from './seed-errors.js';
|
|
5
|
-
import { TestDatabase } from './db-setup.js';
|
|
6
6
|
|
|
7
7
|
describe('ensureBusinessForEntity', () => {
|
|
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
|
-
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
await client.query('BEGIN');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
await client.query('ROLLBACK');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should create a new business for a financial entity', async () => {
|
|
30
|
+
// Create a financial entity first
|
|
31
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
32
|
+
name: 'Test Business Entity',
|
|
33
|
+
type: 'business',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Ensure business for this entity
|
|
37
|
+
await ensureBusinessForEntity(client, entityId);
|
|
38
|
+
|
|
39
|
+
// Verify business exists
|
|
40
|
+
const result = await client.query(
|
|
41
|
+
`SELECT id, no_invoices_required FROM ${qualifyTable('businesses')} WHERE id = $1`,
|
|
42
|
+
[entityId],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(result.rows).toHaveLength(1);
|
|
46
|
+
expect(result.rows[0].id).toBe(entityId);
|
|
47
|
+
expect(result.rows[0].no_invoices_required).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should be idempotent - repeated calls do not create duplicates', async () => {
|
|
51
|
+
// Create a financial entity first
|
|
52
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
53
|
+
name: 'Test Business Entity 2',
|
|
54
|
+
type: 'business',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Call ensureBusinessForEntity multiple times
|
|
58
|
+
await ensureBusinessForEntity(client, entityId);
|
|
59
|
+
await ensureBusinessForEntity(client, entityId);
|
|
60
|
+
await ensureBusinessForEntity(client, entityId);
|
|
61
|
+
|
|
62
|
+
// Verify only one business exists
|
|
63
|
+
const result = await client.query(
|
|
64
|
+
`SELECT id FROM ${qualifyTable('businesses')} WHERE id = $1`,
|
|
65
|
+
[entityId],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(result.rows).toHaveLength(1);
|
|
69
|
+
expect(result.rows[0].id).toBe(entityId);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should support noInvoicesRequired option', async () => {
|
|
73
|
+
// Create a financial entity first
|
|
74
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
75
|
+
name: 'Test Business Entity 3',
|
|
76
|
+
type: 'business',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Ensure business with noInvoicesRequired set to true
|
|
80
|
+
await ensureBusinessForEntity(client, entityId, { noInvoicesRequired: true });
|
|
81
|
+
|
|
82
|
+
// Verify business has correct option set
|
|
83
|
+
const result = await client.query(
|
|
84
|
+
`SELECT id, no_invoices_required FROM ${qualifyTable('businesses')} WHERE id = $1`,
|
|
85
|
+
[entityId],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(result.rows).toHaveLength(1);
|
|
89
|
+
expect(result.rows[0].id).toBe(entityId);
|
|
90
|
+
expect(result.rows[0].no_invoices_required).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should default noInvoicesRequired to false when not specified', async () => {
|
|
94
|
+
// Create a financial entity first
|
|
95
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
96
|
+
name: 'Test Business Entity 4',
|
|
97
|
+
type: 'business',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Ensure business without specifying options
|
|
101
|
+
await ensureBusinessForEntity(client, entityId);
|
|
102
|
+
|
|
103
|
+
// Verify business has default value
|
|
104
|
+
const result = await client.query(
|
|
105
|
+
`SELECT no_invoices_required FROM ${qualifyTable('businesses')} WHERE id = $1`,
|
|
106
|
+
[entityId],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(result.rows).toHaveLength(1);
|
|
110
|
+
expect(result.rows[0].no_invoices_required).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should not modify existing business when called again', async () => {
|
|
114
|
+
// Create a financial entity first
|
|
115
|
+
const { id: entityId } = await ensureFinancialEntity(client, {
|
|
116
|
+
name: 'Test Business Entity 5',
|
|
117
|
+
type: 'business',
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Create business with noInvoicesRequired = true
|
|
121
|
+
await ensureBusinessForEntity(client, entityId, { noInvoicesRequired: true });
|
|
122
|
+
|
|
123
|
+
// Call again with different options (should be no-op)
|
|
124
|
+
await ensureBusinessForEntity(client, entityId, { noInvoicesRequired: false });
|
|
125
|
+
|
|
126
|
+
// Verify original value is preserved
|
|
127
|
+
const result = await client.query(
|
|
128
|
+
`SELECT no_invoices_required FROM ${qualifyTable('businesses')} WHERE id = $1`,
|
|
129
|
+
[entityId],
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(result.rows).toHaveLength(1);
|
|
133
|
+
expect(result.rows[0].no_invoices_required).toBe(true); // Original value preserved
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should not leak data between tests', async () => {
|
|
137
|
+
// This test verifies transactional isolation by checking that data
|
|
138
|
+
// from previous tests is not visible
|
|
139
|
+
const result = await client.query(
|
|
140
|
+
`SELECT COUNT(*) as count FROM ${qualifyTable('businesses')} WHERE id IN (SELECT id FROM ${qualifyTable('financial_entities')} WHERE name LIKE 'Test Business Entity%')`,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Due to ROLLBACK after each test, count should be 0
|
|
144
|
+
expect(parseInt(result.rows[0].count)).toBe(0);
|
|
145
|
+
});
|
|
142
146
|
|
|
143
147
|
// Validation tests
|
|
144
|
-
it('should reject invalid UUID format', async () =>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
}));
|
|
148
|
+
it('should reject invalid UUID format', async () => {
|
|
149
|
+
await expect(
|
|
150
|
+
ensureBusinessForEntity(client, 'not-a-valid-uuid'),
|
|
151
|
+
).rejects.toThrow(EntityValidationError);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should reject non-existent financial entity', async () => {
|
|
155
|
+
const fakeId = '00000000-0000-0000-0000-000000000000';
|
|
156
|
+
|
|
157
|
+
await expect(
|
|
158
|
+
ensureBusinessForEntity(client, fakeId),
|
|
159
|
+
).rejects.toThrow(EntityValidationError);
|
|
160
|
+
});
|
|
159
161
|
});
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import pg from 'pg';
|
|
2
3
|
import { ensureFinancialEntity } from './seed-helpers.js';
|
|
4
|
+
import { testDbConfig } from './test-db-config.js';
|
|
3
5
|
import { withConcurrentTransactions } from './test-transaction.js';
|
|
4
|
-
import { TestDatabase } from './db-setup.js';
|
|
5
6
|
|
|
6
7
|
describe('ensureFinancialEntity - Concurrent Access', () => {
|
|
7
|
-
let
|
|
8
|
+
let pool: pg.Pool;
|
|
8
9
|
|
|
9
10
|
beforeAll(async () => {
|
|
10
|
-
|
|
11
|
-
await db.connect();
|
|
11
|
+
pool = new pg.Pool(testDbConfig);
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
afterAll(async () => {
|
|
15
|
-
await
|
|
15
|
+
await pool.end();
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
it('should handle concurrent inserts of same entity gracefully', async () => {
|
|
@@ -22,7 +22,7 @@ describe('ensureFinancialEntity - Concurrent Access', () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// Two separate transactions attempting to create the same entity
|
|
25
|
-
const [result1, result2] = await withConcurrentTransactions(
|
|
25
|
+
const [result1, result2] = await withConcurrentTransactions(pool, [
|
|
26
26
|
async client => ensureFinancialEntity(client, params),
|
|
27
27
|
async client => ensureFinancialEntity(client, params),
|
|
28
28
|
]);
|
|
@@ -37,7 +37,7 @@ describe('ensureFinancialEntity - Concurrent Access', () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('should handle concurrent inserts of different entities', async () => {
|
|
40
|
-
const [result1, result2, result3] = await withConcurrentTransactions(
|
|
40
|
+
const [result1, result2, result3] = await withConcurrentTransactions(pool, [
|
|
41
41
|
async client =>
|
|
42
42
|
ensureFinancialEntity(client, {
|
|
43
43
|
name: 'Concurrent Entity A',
|
|
@@ -64,8 +64,8 @@ describe('ensureFinancialEntity - Concurrent Access', () => {
|
|
|
64
64
|
it('should handle concurrent creation with separate namespaces', async () => {
|
|
65
65
|
// Test that concurrent operations in separate transactions don't interfere
|
|
66
66
|
const timestamp = Date.now();
|
|
67
|
-
|
|
68
|
-
const [result1, result2, result3] = await withConcurrentTransactions(
|
|
67
|
+
|
|
68
|
+
const [result1, result2, result3] = await withConcurrentTransactions(pool, [
|
|
69
69
|
async client =>
|
|
70
70
|
ensureFinancialEntity(client, {
|
|
71
71
|
name: `Namespace A - ${timestamp}`,
|
|
@@ -87,7 +87,7 @@ describe('ensureFinancialEntity - Concurrent Access', () => {
|
|
|
87
87
|
expect(result1.id).toBeDefined();
|
|
88
88
|
expect(result2.id).toBeDefined();
|
|
89
89
|
expect(result3.id).toBeDefined();
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
// All should have unique IDs
|
|
92
92
|
expect(result1.id).not.toBe(result2.id);
|
|
93
93
|
expect(result2.id).not.toBe(result3.id);
|