@flusys/nestjs-email 6.0.2 → 6.1.1
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/cjs/controllers/email-config.controller.spec.js +85 -0
- package/cjs/controllers/email-send.controller.spec.js +139 -0
- package/cjs/controllers/email-template.controller.spec.js +89 -0
- package/cjs/docs/email-swagger.config.spec.js +62 -0
- package/cjs/entities/index.spec.js +18 -0
- package/cjs/modules/email.module.spec.js +142 -0
- package/cjs/providers/email-factory.service.spec.js +242 -0
- package/cjs/providers/email-provider.registry.spec.js +58 -0
- package/cjs/providers/mailgun-provider.spec.js +244 -0
- package/cjs/providers/sendgrid-provider.spec.js +346 -0
- package/cjs/providers/smtp-provider.js +12 -5
- package/cjs/providers/smtp-provider.spec.js +327 -0
- package/cjs/services/email-config.service.spec.js +69 -0
- package/cjs/services/email-datasource.provider.spec.js +135 -0
- package/cjs/services/email-provider-config.service.spec.js +247 -0
- package/cjs/services/email-send.service.spec.js +546 -0
- package/cjs/services/email-template.service.spec.js +257 -0
- package/cjs/utils/email-templates.util.spec.js +61 -0
- package/fesm/controllers/email-config.controller.spec.js +81 -0
- package/fesm/controllers/email-send.controller.spec.js +135 -0
- package/fesm/controllers/email-template.controller.spec.js +85 -0
- package/fesm/docs/email-swagger.config.spec.js +58 -0
- package/fesm/entities/index.spec.js +14 -0
- package/fesm/modules/email.module.spec.js +138 -0
- package/fesm/providers/email-factory.service.spec.js +238 -0
- package/fesm/providers/email-provider.registry.spec.js +54 -0
- package/fesm/providers/mailgun-provider.spec.js +240 -0
- package/fesm/providers/sendgrid-provider.spec.js +342 -0
- package/fesm/providers/smtp-provider.js +12 -5
- package/fesm/providers/smtp-provider.spec.js +282 -0
- package/fesm/services/email-config.service.spec.js +65 -0
- package/fesm/services/email-datasource.provider.spec.js +131 -0
- package/fesm/services/email-provider-config.service.spec.js +243 -0
- package/fesm/services/email-send.service.spec.js +542 -0
- package/fesm/services/email-template.service.spec.js +253 -0
- package/fesm/utils/email-templates.util.spec.js +57 -0
- package/package.json +3 -3
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _emailconstants = require("../config/email.constants");
|
|
6
|
+
const _emailconfigservice = require("./email-config.service");
|
|
7
|
+
function buildOptions(overrides = {}) {
|
|
8
|
+
return {
|
|
9
|
+
bootstrapAppConfig: undefined,
|
|
10
|
+
config: undefined,
|
|
11
|
+
...overrides
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
describe('EmailConfigService', ()=>{
|
|
15
|
+
describe('isCompanyFeatureEnabled', ()=>{
|
|
16
|
+
it('returns false when bootstrapAppConfig is not provided', ()=>{
|
|
17
|
+
const service = new _emailconfigservice.EmailConfigService(buildOptions());
|
|
18
|
+
expect(service.isCompanyFeatureEnabled()).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it('returns false when enableCompanyFeature is explicitly false', ()=>{
|
|
21
|
+
const service = new _emailconfigservice.EmailConfigService(buildOptions({
|
|
22
|
+
bootstrapAppConfig: {
|
|
23
|
+
enableCompanyFeature: false
|
|
24
|
+
}
|
|
25
|
+
}));
|
|
26
|
+
expect(service.isCompanyFeatureEnabled()).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it('returns true when enableCompanyFeature is true', ()=>{
|
|
29
|
+
const service = new _emailconfigservice.EmailConfigService(buildOptions({
|
|
30
|
+
bootstrapAppConfig: {
|
|
31
|
+
enableCompanyFeature: true
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
expect(service.isCompanyFeatureEnabled()).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('getDatabaseMode / isMultiTenant', ()=>{
|
|
38
|
+
it('defaults to "single" when bootstrapAppConfig is missing', ()=>{
|
|
39
|
+
const service = new _emailconfigservice.EmailConfigService(buildOptions());
|
|
40
|
+
expect(service.getDatabaseMode()).toBe('single');
|
|
41
|
+
expect(service.isMultiTenant()).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it('reflects "multi-tenant" mode from bootstrapAppConfig', ()=>{
|
|
44
|
+
const service = new _emailconfigservice.EmailConfigService(buildOptions({
|
|
45
|
+
bootstrapAppConfig: {
|
|
46
|
+
databaseMode: 'multi-tenant'
|
|
47
|
+
}
|
|
48
|
+
}));
|
|
49
|
+
expect(service.getDatabaseMode()).toBe('multi-tenant');
|
|
50
|
+
expect(service.isMultiTenant()).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('getDefaultFromName', ()=>{
|
|
54
|
+
it('always returns the module-level DEFAULT_FROM_NAME constant', ()=>{
|
|
55
|
+
const service = new _emailconfigservice.EmailConfigService(buildOptions());
|
|
56
|
+
expect(service.getDefaultFromName()).toBe(_emailconstants.DEFAULT_FROM_NAME);
|
|
57
|
+
expect(service.getDefaultFromName()).toBe('FLUSYS');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('getOptions', ()=>{
|
|
61
|
+
it('returns the exact options object injected via the constructor', ()=>{
|
|
62
|
+
const options = buildOptions({
|
|
63
|
+
defaultProvider: 'smtp'
|
|
64
|
+
});
|
|
65
|
+
const service = new _emailconfigservice.EmailConfigService(options);
|
|
66
|
+
expect(service.getOptions()).toBe(options);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _modules = require("@flusys/nestjs-shared/modules");
|
|
6
|
+
const _entities = require("../entities");
|
|
7
|
+
const _emaildatasourceprovider = require("./email-datasource.provider");
|
|
8
|
+
function buildConfigService(overrides = {}) {
|
|
9
|
+
const options = {
|
|
10
|
+
bootstrapAppConfig: {
|
|
11
|
+
databaseMode: 'single',
|
|
12
|
+
enableCompanyFeature: false
|
|
13
|
+
},
|
|
14
|
+
config: {
|
|
15
|
+
defaultDatabaseConfig: {
|
|
16
|
+
type: 'mysql',
|
|
17
|
+
host: 'localhost',
|
|
18
|
+
database: 'flusys'
|
|
19
|
+
},
|
|
20
|
+
tenants: []
|
|
21
|
+
},
|
|
22
|
+
...overrides
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
isCompanyFeatureEnabled: jest.fn().mockReturnValue(options.bootstrapAppConfig?.enableCompanyFeature ?? false),
|
|
26
|
+
getDatabaseMode: jest.fn().mockReturnValue(options.bootstrapAppConfig?.databaseMode ?? 'single'),
|
|
27
|
+
isMultiTenant: jest.fn().mockReturnValue(options.bootstrapAppConfig?.databaseMode === 'multi-tenant'),
|
|
28
|
+
getDefaultFromName: jest.fn().mockReturnValue('FLUSYS'),
|
|
29
|
+
getOptions: jest.fn().mockReturnValue(options)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
describe('EmailDataSourceProvider', ()=>{
|
|
33
|
+
afterEach(()=>{
|
|
34
|
+
jest.restoreAllMocks();
|
|
35
|
+
// Reset static state isolated to this subclass between tests.
|
|
36
|
+
_emaildatasourceprovider.EmailDataSourceProvider.singleDataSource = null;
|
|
37
|
+
_emaildatasourceprovider.EmailDataSourceProvider.initialized = false;
|
|
38
|
+
_emaildatasourceprovider.EmailDataSourceProvider.tenantsRegistry.clear();
|
|
39
|
+
});
|
|
40
|
+
it('propagates bootstrapAppConfig from EmailConfigService into the base data source service', ()=>{
|
|
41
|
+
const configService = buildConfigService({
|
|
42
|
+
bootstrapAppConfig: {
|
|
43
|
+
databaseMode: 'multi-tenant',
|
|
44
|
+
enableCompanyFeature: true
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
48
|
+
expect(provider.isMultiTenant()).toBe(true);
|
|
49
|
+
expect(provider.getDatabaseMode()).toBe('multi-tenant');
|
|
50
|
+
});
|
|
51
|
+
it('defaults to single-database mode when bootstrapAppConfig is absent', ()=>{
|
|
52
|
+
const configService = buildConfigService({
|
|
53
|
+
bootstrapAppConfig: undefined
|
|
54
|
+
});
|
|
55
|
+
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
56
|
+
expect(provider.isMultiTenant()).toBe(false);
|
|
57
|
+
expect(provider.getDatabaseMode()).toBe('single');
|
|
58
|
+
});
|
|
59
|
+
describe('getEnableCompanyFeatureForTenant', ()=>{
|
|
60
|
+
it('falls back to EmailConfigService.isCompanyFeatureEnabled() when no tenant is given', ()=>{
|
|
61
|
+
const configService = buildConfigService({
|
|
62
|
+
bootstrapAppConfig: {
|
|
63
|
+
databaseMode: 'single',
|
|
64
|
+
enableCompanyFeature: true
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
68
|
+
expect(provider.getEnableCompanyFeatureForTenant(undefined)).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
it('prefers the tenant-level enableCompanyFeature flag over the module default', ()=>{
|
|
71
|
+
const configService = buildConfigService({
|
|
72
|
+
bootstrapAppConfig: {
|
|
73
|
+
databaseMode: 'multi-tenant',
|
|
74
|
+
enableCompanyFeature: true
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
78
|
+
expect(provider.getEnableCompanyFeatureForTenant({
|
|
79
|
+
id: 'tenant-1',
|
|
80
|
+
database: 'tenant_db',
|
|
81
|
+
enableCompanyFeature: false
|
|
82
|
+
})).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
it('uses the module default when the tenant does not specify enableCompanyFeature', ()=>{
|
|
85
|
+
const configService = buildConfigService({
|
|
86
|
+
bootstrapAppConfig: {
|
|
87
|
+
databaseMode: 'multi-tenant',
|
|
88
|
+
enableCompanyFeature: true
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
92
|
+
expect(provider.getEnableCompanyFeatureForTenant({
|
|
93
|
+
id: 'tenant-1',
|
|
94
|
+
database: 'tenant_db'
|
|
95
|
+
})).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('createDataSourceFromConfig', ()=>{
|
|
99
|
+
it('resolves core (non-company) entities when the company feature is disabled', async ()=>{
|
|
100
|
+
const configService = buildConfigService({
|
|
101
|
+
bootstrapAppConfig: {
|
|
102
|
+
databaseMode: 'single',
|
|
103
|
+
enableCompanyFeature: false
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
107
|
+
const fakeDataSource = {};
|
|
108
|
+
const superSpy = jest.spyOn(_modules.MultiTenantDataSourceService.prototype, 'createDataSourceFromConfig').mockResolvedValue(fakeDataSource);
|
|
109
|
+
const config = {
|
|
110
|
+
type: 'mysql',
|
|
111
|
+
database: 'flusys'
|
|
112
|
+
};
|
|
113
|
+
const result = await provider.createDataSourceFromConfig(config);
|
|
114
|
+
expect(result).toBe(fakeDataSource);
|
|
115
|
+
expect(superSpy).toHaveBeenCalledWith(config, _entities.EmailCoreEntities);
|
|
116
|
+
});
|
|
117
|
+
it('resolves company-aware entities when the company feature is enabled', async ()=>{
|
|
118
|
+
const configService = buildConfigService({
|
|
119
|
+
bootstrapAppConfig: {
|
|
120
|
+
databaseMode: 'single',
|
|
121
|
+
enableCompanyFeature: true
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
125
|
+
const fakeDataSource = {};
|
|
126
|
+
const superSpy = jest.spyOn(_modules.MultiTenantDataSourceService.prototype, 'createDataSourceFromConfig').mockResolvedValue(fakeDataSource);
|
|
127
|
+
const config = {
|
|
128
|
+
type: 'mysql',
|
|
129
|
+
database: 'flusys'
|
|
130
|
+
};
|
|
131
|
+
await provider.createDataSourceFromConfig(config);
|
|
132
|
+
expect(superSpy).toHaveBeenCalledWith(config, _entities.EmailCompanyEntities);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _testing = require("@nestjs/testing");
|
|
6
|
+
const _modules = require("@flusys/nestjs-shared/modules");
|
|
7
|
+
const _repositorymock = require("@test-utils/mocks/repository.mock");
|
|
8
|
+
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
9
|
+
const _entities = require("../entities");
|
|
10
|
+
const _emailconfigservice = require("./email-config.service");
|
|
11
|
+
const _emaildatasourceprovider = require("./email-datasource.provider");
|
|
12
|
+
const _emailproviderconfigservice = require("./email-provider-config.service");
|
|
13
|
+
function buildEmailConfig(overrides = {}) {
|
|
14
|
+
return {
|
|
15
|
+
id: 'config-uuid-1',
|
|
16
|
+
name: 'Primary SMTP',
|
|
17
|
+
provider: 'smtp',
|
|
18
|
+
config: {
|
|
19
|
+
host: 'smtp.example.com'
|
|
20
|
+
},
|
|
21
|
+
fromEmail: 'noreply@example.com',
|
|
22
|
+
fromName: 'FLUSYS',
|
|
23
|
+
isActive: true,
|
|
24
|
+
isDefault: false,
|
|
25
|
+
createdAt: new Date(),
|
|
26
|
+
updatedAt: new Date(),
|
|
27
|
+
deletedAt: null,
|
|
28
|
+
createdById: null,
|
|
29
|
+
updatedById: null,
|
|
30
|
+
deletedById: null,
|
|
31
|
+
...overrides
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function buildQuery() {
|
|
35
|
+
return {
|
|
36
|
+
select: jest.fn().mockReturnThis(),
|
|
37
|
+
andWhere: jest.fn().mockReturnThis(),
|
|
38
|
+
orderBy: jest.fn().mockReturnThis()
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
describe('EmailProviderConfigService', ()=>{
|
|
42
|
+
let service;
|
|
43
|
+
let mockRepo;
|
|
44
|
+
let mockEmailConfig;
|
|
45
|
+
beforeEach(async ()=>{
|
|
46
|
+
mockRepo = (0, _repositorymock.createMockRepository)();
|
|
47
|
+
const mockDataSourceProvider = (0, _repositorymock.createMockDataSourceProvider)(mockRepo);
|
|
48
|
+
mockEmailConfig = {
|
|
49
|
+
isCompanyFeatureEnabled: jest.fn().mockReturnValue(false)
|
|
50
|
+
};
|
|
51
|
+
const module = await _testing.Test.createTestingModule({
|
|
52
|
+
providers: [
|
|
53
|
+
_emailproviderconfigservice.EmailProviderConfigService,
|
|
54
|
+
{
|
|
55
|
+
provide: 'CACHE_INSTANCE',
|
|
56
|
+
useValue: {}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
provide: _modules.UtilsService,
|
|
60
|
+
useValue: {}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
provide: _emailconfigservice.EmailConfigService,
|
|
64
|
+
useValue: mockEmailConfig
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
provide: _emaildatasourceprovider.EmailDataSourceProvider,
|
|
68
|
+
useValue: mockDataSourceProvider
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}).compile();
|
|
72
|
+
service = await module.resolve(_emailproviderconfigservice.EmailProviderConfigService);
|
|
73
|
+
});
|
|
74
|
+
describe('resolveEntity', ()=>{
|
|
75
|
+
it('resolves the plain EmailConfig entity when the company feature is disabled', ()=>{
|
|
76
|
+
expect(service.resolveEntity()).toBe(_entities.EmailConfig);
|
|
77
|
+
});
|
|
78
|
+
it('resolves EmailConfigWithCompany when the company feature is enabled', ()=>{
|
|
79
|
+
mockEmailConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
80
|
+
expect(service.resolveEntity()).toBe(_entities.EmailConfigWithCompany);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('convertSingleDtoToEntity', ()=>{
|
|
84
|
+
it('does not attach companyId when the company feature is disabled', async ()=>{
|
|
85
|
+
const entity = await service.convertSingleDtoToEntity({
|
|
86
|
+
name: 'X',
|
|
87
|
+
provider: 'smtp',
|
|
88
|
+
config: {}
|
|
89
|
+
}, (0, _loggedusermock.buildMockUser)());
|
|
90
|
+
expect(entity.companyId).toBeUndefined();
|
|
91
|
+
});
|
|
92
|
+
it('attaches the current user companyId when the company feature is enabled', async ()=>{
|
|
93
|
+
mockEmailConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
94
|
+
const entity = await service.convertSingleDtoToEntity({
|
|
95
|
+
name: 'X',
|
|
96
|
+
provider: 'smtp',
|
|
97
|
+
config: {}
|
|
98
|
+
}, (0, _loggedusermock.buildMockUser)({
|
|
99
|
+
companyId: 'company-uuid-42'
|
|
100
|
+
}));
|
|
101
|
+
expect(entity.companyId).toBe('company-uuid-42');
|
|
102
|
+
});
|
|
103
|
+
it('sets companyId to null when the company feature is enabled but no user is provided', async ()=>{
|
|
104
|
+
mockEmailConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
105
|
+
const entity = await service.convertSingleDtoToEntity({
|
|
106
|
+
name: 'X',
|
|
107
|
+
provider: 'smtp',
|
|
108
|
+
config: {}
|
|
109
|
+
}, null);
|
|
110
|
+
expect(entity.companyId).toBeNull();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('getSelectQuery', ()=>{
|
|
114
|
+
it('selects the default whitelisted fields (without companyId) when company feature is disabled', async ()=>{
|
|
115
|
+
const query = buildQuery();
|
|
116
|
+
const { isRaw } = await service.getSelectQuery(query, (0, _loggedusermock.buildMockUser)());
|
|
117
|
+
expect(isRaw).toBe(false);
|
|
118
|
+
const [selected] = query.select.mock.calls[0];
|
|
119
|
+
expect(selected).toEqual(expect.arrayContaining([
|
|
120
|
+
'emailConfig.id',
|
|
121
|
+
'emailConfig.name',
|
|
122
|
+
'emailConfig.provider',
|
|
123
|
+
'emailConfig.config',
|
|
124
|
+
'emailConfig.fromEmail',
|
|
125
|
+
'emailConfig.fromName',
|
|
126
|
+
'emailConfig.isActive',
|
|
127
|
+
'emailConfig.isDefault',
|
|
128
|
+
'emailConfig.createdAt',
|
|
129
|
+
'emailConfig.updatedAt'
|
|
130
|
+
]));
|
|
131
|
+
expect(selected).not.toContain('emailConfig.companyId');
|
|
132
|
+
});
|
|
133
|
+
it('appends companyId to the default field list when company feature is enabled', async ()=>{
|
|
134
|
+
mockEmailConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
135
|
+
const query = buildQuery();
|
|
136
|
+
await service.getSelectQuery(query, (0, _loggedusermock.buildMockUser)());
|
|
137
|
+
const [selected] = query.select.mock.calls[0];
|
|
138
|
+
expect(selected).toContain('emailConfig.companyId');
|
|
139
|
+
});
|
|
140
|
+
it('respects an explicit select list instead of the defaults', async ()=>{
|
|
141
|
+
const query = buildQuery();
|
|
142
|
+
await service.getSelectQuery(query, (0, _loggedusermock.buildMockUser)(), [
|
|
143
|
+
'name'
|
|
144
|
+
]);
|
|
145
|
+
expect(query.select).toHaveBeenCalledWith([
|
|
146
|
+
'emailConfig.name'
|
|
147
|
+
]);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
describe('getExtraManipulateQuery', ()=>{
|
|
151
|
+
it('applies the company filter and orders by createdAt DESC when company feature is enabled', async ()=>{
|
|
152
|
+
mockEmailConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
153
|
+
const query = buildQuery();
|
|
154
|
+
await service.getExtraManipulateQuery(query, {}, (0, _loggedusermock.buildMockUser)({
|
|
155
|
+
companyId: 'c-1'
|
|
156
|
+
}));
|
|
157
|
+
expect(query.andWhere).toHaveBeenCalledWith('emailConfig.companyId = :companyId', {
|
|
158
|
+
companyId: 'c-1'
|
|
159
|
+
});
|
|
160
|
+
expect(query.orderBy).toHaveBeenCalledWith('emailConfig.createdAt', 'DESC');
|
|
161
|
+
});
|
|
162
|
+
it('skips the company filter when the company feature is disabled', async ()=>{
|
|
163
|
+
const query = buildQuery();
|
|
164
|
+
await service.getExtraManipulateQuery(query, {}, (0, _loggedusermock.buildMockUser)());
|
|
165
|
+
expect(query.andWhere).not.toHaveBeenCalled();
|
|
166
|
+
expect(query.orderBy).toHaveBeenCalledWith('emailConfig.createdAt', 'DESC');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('findByIdDirect', ()=>{
|
|
170
|
+
it('returns the matching config from the repository', async ()=>{
|
|
171
|
+
const config = buildEmailConfig();
|
|
172
|
+
mockRepo.findOne.mockResolvedValue(config);
|
|
173
|
+
const result = await service.findByIdDirect('config-uuid-1');
|
|
174
|
+
expect(result).toBe(config);
|
|
175
|
+
expect(mockRepo.findOne).toHaveBeenCalledWith({
|
|
176
|
+
where: {
|
|
177
|
+
id: 'config-uuid-1'
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
it('returns null when no config matches', async ()=>{
|
|
182
|
+
mockRepo.findOne.mockResolvedValue(null);
|
|
183
|
+
await expect(service.findByIdDirect('missing')).resolves.toBeNull();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('getDefaultConfig', ()=>{
|
|
187
|
+
it('returns the active default config when one is marked isDefault', async ()=>{
|
|
188
|
+
const defaultConfig = buildEmailConfig({
|
|
189
|
+
isDefault: true
|
|
190
|
+
});
|
|
191
|
+
mockRepo.findOne.mockResolvedValueOnce(defaultConfig);
|
|
192
|
+
const result = await service.getDefaultConfig((0, _loggedusermock.buildMockUser)());
|
|
193
|
+
expect(result).toBe(defaultConfig);
|
|
194
|
+
expect(mockRepo.findOne).toHaveBeenCalledTimes(1);
|
|
195
|
+
expect(mockRepo.findOne).toHaveBeenCalledWith({
|
|
196
|
+
where: {
|
|
197
|
+
isActive: true,
|
|
198
|
+
isDefault: true
|
|
199
|
+
},
|
|
200
|
+
order: {
|
|
201
|
+
createdAt: 'ASC'
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
it('falls back to the oldest active config when no default is marked', async ()=>{
|
|
206
|
+
const fallbackConfig = buildEmailConfig({
|
|
207
|
+
isDefault: false
|
|
208
|
+
});
|
|
209
|
+
mockRepo.findOne.mockResolvedValueOnce(null).mockResolvedValueOnce(fallbackConfig);
|
|
210
|
+
const result = await service.getDefaultConfig((0, _loggedusermock.buildMockUser)());
|
|
211
|
+
expect(result).toBe(fallbackConfig);
|
|
212
|
+
expect(mockRepo.findOne).toHaveBeenCalledTimes(2);
|
|
213
|
+
expect(mockRepo.findOne).toHaveBeenNthCalledWith(2, {
|
|
214
|
+
where: {
|
|
215
|
+
isActive: true
|
|
216
|
+
},
|
|
217
|
+
order: {
|
|
218
|
+
createdAt: 'ASC'
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
it('returns null when there is no active config at all', async ()=>{
|
|
223
|
+
mockRepo.findOne.mockResolvedValue(null);
|
|
224
|
+
await expect(service.getDefaultConfig((0, _loggedusermock.buildMockUser)())).resolves.toBeNull();
|
|
225
|
+
});
|
|
226
|
+
it('scopes the lookup to the current company when the company feature is enabled', async ()=>{
|
|
227
|
+
mockEmailConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
228
|
+
const defaultConfig = buildEmailConfig({
|
|
229
|
+
isDefault: true
|
|
230
|
+
});
|
|
231
|
+
mockRepo.findOne.mockResolvedValueOnce(defaultConfig);
|
|
232
|
+
await service.getDefaultConfig((0, _loggedusermock.buildMockUser)({
|
|
233
|
+
companyId: 'company-uuid-9'
|
|
234
|
+
}));
|
|
235
|
+
expect(mockRepo.findOne).toHaveBeenCalledWith({
|
|
236
|
+
where: {
|
|
237
|
+
isActive: true,
|
|
238
|
+
companyId: 'company-uuid-9',
|
|
239
|
+
isDefault: true
|
|
240
|
+
},
|
|
241
|
+
order: {
|
|
242
|
+
createdAt: 'ASC'
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
});
|