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