opticedge-cloud-utils 1.1.18 → 1.1.20
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/dist/src/number.d.ts +1 -0
- package/dist/src/number.js +6 -0
- package/dist/{pub.js → src/pub.js} +5 -7
- package/dist/tests/auth.test.d.ts +1 -0
- package/dist/tests/auth.test.js +79 -0
- package/dist/tests/chunk.test.d.ts +1 -0
- package/dist/tests/chunk.test.js +45 -0
- package/dist/tests/db/mongo.test.d.ts +1 -0
- package/dist/tests/db/mongo.test.js +43 -0
- package/dist/tests/db/mongo2.test.d.ts +1 -0
- package/dist/tests/db/mongo2.test.js +49 -0
- package/dist/tests/db/mongo3.test.d.ts +1 -0
- package/dist/tests/db/mongo3.test.js +60 -0
- package/dist/tests/env.test.d.ts +1 -0
- package/dist/tests/env.test.js +17 -0
- package/dist/tests/number.test.d.ts +1 -0
- package/dist/tests/number.test.js +30 -0
- package/dist/tests/parser.test.d.ts +1 -0
- package/dist/tests/parser.test.js +24 -0
- package/dist/tests/pub.test.d.ts +1 -0
- package/dist/tests/pub.test.js +102 -0
- package/dist/tests/regex.test.d.ts +1 -0
- package/dist/tests/regex.test.js +60 -0
- package/dist/tests/retry.test.d.ts +1 -0
- package/dist/tests/retry.test.js +339 -0
- package/dist/tests/secrets.test.d.ts +1 -0
- package/dist/tests/secrets.test.js +38 -0
- package/dist/tests/task.test.d.ts +1 -0
- package/dist/tests/task.test.js +262 -0
- package/dist/tests/tw/utils.test.d.ts +1 -0
- package/dist/tests/tw/utils.test.js +26 -0
- package/dist/tests/tw/wallet.test.d.ts +1 -0
- package/dist/tests/tw/wallet.test.js +108 -0
- package/dist/tests/validator.d.ts +1 -0
- package/dist/tests/validator.js +34 -0
- package/package.json +1 -1
- package/src/number.ts +3 -0
- package/src/pub.ts +7 -9
- package/tests/number.test.ts +34 -0
- package/tests/pub.test.ts +47 -5
- package/tsconfig.json +1 -1
- /package/dist/{auth.d.ts → src/auth.d.ts} +0 -0
- /package/dist/{auth.js → src/auth.js} +0 -0
- /package/dist/{chunk.d.ts → src/chunk.d.ts} +0 -0
- /package/dist/{chunk.js → src/chunk.js} +0 -0
- /package/dist/{db → src/db}/mongo.d.ts +0 -0
- /package/dist/{db → src/db}/mongo.js +0 -0
- /package/dist/{db → src/db}/mongo2.d.ts +0 -0
- /package/dist/{db → src/db}/mongo2.js +0 -0
- /package/dist/{db → src/db}/mongo3.d.ts +0 -0
- /package/dist/{db → src/db}/mongo3.js +0 -0
- /package/dist/{env.d.ts → src/env.d.ts} +0 -0
- /package/dist/{env.js → src/env.js} +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{index.js → src/index.js} +0 -0
- /package/dist/{parser.d.ts → src/parser.d.ts} +0 -0
- /package/dist/{parser.js → src/parser.js} +0 -0
- /package/dist/{pub.d.ts → src/pub.d.ts} +0 -0
- /package/dist/{regex.d.ts → src/regex.d.ts} +0 -0
- /package/dist/{regex.js → src/regex.js} +0 -0
- /package/dist/{retry.d.ts → src/retry.d.ts} +0 -0
- /package/dist/{retry.js → src/retry.js} +0 -0
- /package/dist/{secrets.d.ts → src/secrets.d.ts} +0 -0
- /package/dist/{secrets.js → src/secrets.js} +0 -0
- /package/dist/{task.d.ts → src/task.d.ts} +0 -0
- /package/dist/{task.js → src/task.js} +0 -0
- /package/dist/{tw → src/tw}/utils.d.ts +0 -0
- /package/dist/{tw → src/tw}/utils.js +0 -0
- /package/dist/{tw → src/tw}/wallet.d.ts +0 -0
- /package/dist/{tw → src/tw}/wallet.js +0 -0
- /package/dist/{validator.d.ts → src/validator.d.ts} +0 -0
- /package/dist/{validator.js → src/validator.js} +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const utils_1 = require("../../src/tw/utils");
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
describe('isValidWebhookSignature', () => {
|
|
9
|
+
const secret = 'test_secret';
|
|
10
|
+
const body = '{"message":"hello"}';
|
|
11
|
+
it('returns true for a valid signature', () => {
|
|
12
|
+
const validSignature = crypto_1.default.createHmac('sha256', secret).update(body).digest('hex');
|
|
13
|
+
expect((0, utils_1.isValidWebhookSignature)(secret, body, validSignature)).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it('returns false for an invalid signature', () => {
|
|
16
|
+
const invalidSignature = 'invalidsignature123';
|
|
17
|
+
expect((0, utils_1.isValidWebhookSignature)(secret, body, invalidSignature)).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
it('returns false if body or secret is tampered', () => {
|
|
20
|
+
const originalSignature = crypto_1.default.createHmac('sha256', secret).update(body).digest('hex');
|
|
21
|
+
// wrong body
|
|
22
|
+
expect((0, utils_1.isValidWebhookSignature)(secret, '{"message":"tampered"}', originalSignature)).toBe(false);
|
|
23
|
+
// wrong secret
|
|
24
|
+
expect((0, utils_1.isValidWebhookSignature)('wrong_secret', body, originalSignature)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// tests/tw/wallet.test.ts
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
const wallet_1 = require("../../src/tw/wallet");
|
|
10
|
+
jest.mock('axios');
|
|
11
|
+
const mockedAxios = axios_1.default;
|
|
12
|
+
function makeAxiosError(message, status, noResponse = false) {
|
|
13
|
+
const err = new Error(message);
|
|
14
|
+
err.isAxiosError = true;
|
|
15
|
+
if (!noResponse) {
|
|
16
|
+
err.response = {
|
|
17
|
+
status: typeof status === 'number' ? status : 500,
|
|
18
|
+
data: { message: `mocked ${status ?? 500}` }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// simulate network error (no response)
|
|
23
|
+
delete err.response;
|
|
24
|
+
}
|
|
25
|
+
return err;
|
|
26
|
+
}
|
|
27
|
+
function makeSuccessResponse(data) {
|
|
28
|
+
return { data, status: 200 };
|
|
29
|
+
}
|
|
30
|
+
describe('pregenerateInAppWallet (current signature)', () => {
|
|
31
|
+
const mockClientId = 'test-client-id';
|
|
32
|
+
const mockSecretKey = 'test-secret-key';
|
|
33
|
+
const mockEmail = 'user@example.com';
|
|
34
|
+
const mockWalletAddress = '0xabc123';
|
|
35
|
+
const realMathRandom = Math.random;
|
|
36
|
+
const realWarn = console.warn;
|
|
37
|
+
const realError = console.error;
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
// make retry jitter deterministic (so delays don't interfere)
|
|
41
|
+
Math.random = jest.fn(() => 0);
|
|
42
|
+
// axios.isAxiosError helper
|
|
43
|
+
jest
|
|
44
|
+
.spyOn(axios_1.default, 'isAxiosError')
|
|
45
|
+
.mockImplementation((e) => Boolean(e && e.isAxiosError));
|
|
46
|
+
// silence logs during tests but keep spies
|
|
47
|
+
console.warn = jest.fn();
|
|
48
|
+
console.error = jest.fn();
|
|
49
|
+
});
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
Math.random = realMathRandom;
|
|
52
|
+
console.warn = realWarn;
|
|
53
|
+
console.error = realError;
|
|
54
|
+
});
|
|
55
|
+
it('returns null and logs if params missing', async () => {
|
|
56
|
+
const res = await (0, wallet_1.pregenerateInAppWallet)('', mockSecretKey, mockEmail);
|
|
57
|
+
expect(mockedAxios.post).not.toHaveBeenCalled();
|
|
58
|
+
expect(res).toBeNull();
|
|
59
|
+
expect(console.error.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
60
|
+
expect(console.error.mock.calls[0][0]).toEqual('Missing client id or secret or email');
|
|
61
|
+
});
|
|
62
|
+
it('retries on 500 then succeeds and returns address', async () => {
|
|
63
|
+
// first: 500, then success
|
|
64
|
+
mockedAxios.post
|
|
65
|
+
.mockRejectedValueOnce(makeAxiosError('server error', 500))
|
|
66
|
+
.mockResolvedValueOnce({ data: { wallet: { address: mockWalletAddress } } });
|
|
67
|
+
const result = await (0, wallet_1.pregenerateInAppWallet)(mockClientId, mockSecretKey, mockEmail);
|
|
68
|
+
expect(mockedAxios.post).toHaveBeenCalledTimes(2);
|
|
69
|
+
expect(result).toBe(mockWalletAddress);
|
|
70
|
+
expect(console.warn.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
71
|
+
});
|
|
72
|
+
it('does NOT retry on 400 and returns null immediately', async () => {
|
|
73
|
+
mockedAxios.post.mockRejectedValue(makeAxiosError('bad request', 400));
|
|
74
|
+
const result = await (0, wallet_1.pregenerateInAppWallet)(mockClientId, mockSecretKey, mockEmail);
|
|
75
|
+
expect(mockedAxios.post).toHaveBeenCalledTimes(1);
|
|
76
|
+
expect(result).toBeNull();
|
|
77
|
+
// no retry log
|
|
78
|
+
expect(console.warn.mock.calls.length).toBe(0);
|
|
79
|
+
});
|
|
80
|
+
it('retries on network/no-response errors and then succeeds', async () => {
|
|
81
|
+
mockedAxios.post
|
|
82
|
+
.mockRejectedValueOnce(makeAxiosError('network down', undefined, true))
|
|
83
|
+
.mockResolvedValueOnce({ data: { wallet: { address: mockWalletAddress } } });
|
|
84
|
+
const result = await (0, wallet_1.pregenerateInAppWallet)(mockClientId, mockSecretKey, mockEmail);
|
|
85
|
+
expect(mockedAxios.post).toHaveBeenCalledTimes(2);
|
|
86
|
+
expect(result).toBe(mockWalletAddress);
|
|
87
|
+
});
|
|
88
|
+
it('exhausts retries on repeated 500s and returns null', async () => {
|
|
89
|
+
mockedAxios.post.mockRejectedValue(makeAxiosError('server error', 500));
|
|
90
|
+
const result = await (0, wallet_1.pregenerateInAppWallet)(mockClientId, mockSecretKey, mockEmail);
|
|
91
|
+
// retry behavior is implementation-dependent; assert at least one call and final null
|
|
92
|
+
expect(mockedAxios.post.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
93
|
+
expect(result).toBeNull();
|
|
94
|
+
expect(console.error.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
95
|
+
});
|
|
96
|
+
it('logs invalid wallet shape and returns null when wallet.address missing (2xx response)', async () => {
|
|
97
|
+
mockedAxios.post.mockResolvedValueOnce(makeSuccessResponse({ wallet: {} }));
|
|
98
|
+
const result = await (0, wallet_1.pregenerateInAppWallet)(mockClientId, mockSecretKey, mockEmail);
|
|
99
|
+
expect(mockedAxios.post).toHaveBeenCalledTimes(1);
|
|
100
|
+
expect(result).toBeNull();
|
|
101
|
+
// ensure we logged the invalid shape (the implementation logs then throws)
|
|
102
|
+
expect(console.error.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
103
|
+
const firstArg = console.error.mock.calls[0][0];
|
|
104
|
+
expect(firstArg).toEqual('Invalid wallet response shape');
|
|
105
|
+
const secondArg = console.error.mock.calls[0][1];
|
|
106
|
+
expect(secondArg).toEqual(expect.objectContaining({ status: 200, data: { wallet: {} } }));
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// isValidEmail.test.ts
|
|
4
|
+
const globals_1 = require("@jest/globals");
|
|
5
|
+
const src_1 = require("../src");
|
|
6
|
+
(0, globals_1.describe)('isValidEmail', () => {
|
|
7
|
+
(0, globals_1.it)('returns true for simple valid emails', () => {
|
|
8
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('test@example.com')).toBe(true);
|
|
9
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user.name@domain.co')).toBe(true);
|
|
10
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user_name+tag@sub.domain.org')).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
(0, globals_1.it)('returns false for missing parts', () => {
|
|
13
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('')).toBe(false);
|
|
14
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('plainaddress')).toBe(false);
|
|
15
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('@no-local-part.com')).toBe(false);
|
|
16
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('username@')).toBe(false);
|
|
17
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user@domain')).toBe(false);
|
|
18
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('username@.com')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
(0, globals_1.it)('handles whitespace correctly', () => {
|
|
21
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)(' test@example.com ')).toBe(true); // trims input
|
|
22
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('\nuser@domain.com\t')).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
(0, globals_1.it)('rejects invalid characters or formats', () => {
|
|
25
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user@@domain.com')).toBe(false);
|
|
26
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user@domain,com')).toBe(false);
|
|
27
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user@domain..com')).toBe(false);
|
|
28
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user@.domain.com')).toBe(false);
|
|
29
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('user@domain com')).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
(0, globals_1.it)('accepts minimal valid domain structures', () => {
|
|
32
|
+
(0, globals_1.expect)((0, src_1.isValidEmail)('x@y.z')).toBe(true); // still valid
|
|
33
|
+
});
|
|
34
|
+
});
|
package/package.json
CHANGED
package/src/number.ts
ADDED
package/src/pub.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// src/pub.ts
|
|
2
2
|
import { PubSub } from '@google-cloud/pubsub'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
let cachedPubSub: PubSub | null = null
|
|
5
5
|
|
|
6
6
|
function getPubSub(projectId: string): PubSub {
|
|
7
7
|
if (!projectId) throw new Error('projectId is required')
|
|
8
|
-
|
|
9
|
-
if (!
|
|
10
|
-
|
|
11
|
-
pubsubCache.set(projectId, ps)
|
|
8
|
+
|
|
9
|
+
if (!cachedPubSub) {
|
|
10
|
+
cachedPubSub = new PubSub({ projectId })
|
|
12
11
|
}
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
return cachedPubSub
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export async function publishMessage(
|
|
@@ -30,9 +30,7 @@ export async function publishMessage(
|
|
|
30
30
|
data
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
console.info(
|
|
34
|
-
`INFO: Pub/Sub publish project=${projectId} topic=${topicName} msg_id=${messageId}`
|
|
35
|
-
)
|
|
33
|
+
console.info(`INFO: Pub/Sub publish topic=${topicName} msg_id=${messageId}`)
|
|
36
34
|
return messageId
|
|
37
35
|
} catch (err) {
|
|
38
36
|
console.error('ERROR: publish failed:', err)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { round1 } from '../src/number'
|
|
2
|
+
|
|
3
|
+
describe('round1', () => {
|
|
4
|
+
it('rounds down correctly', () => {
|
|
5
|
+
expect(round1(4.21)).toBe(4.2)
|
|
6
|
+
expect(round1(4.24)).toBe(4.2)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('rounds up correctly', () => {
|
|
10
|
+
expect(round1(4.25)).toBe(4.3)
|
|
11
|
+
expect(round1(4.26)).toBe(4.3)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('handles floating point precision issues', () => {
|
|
15
|
+
expect(round1(1.005)).toBe(1.0) // classic float issue
|
|
16
|
+
expect(round1(2.675)).toBe(2.7)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('handles negative numbers', () => {
|
|
20
|
+
expect(round1(-4.24)).toBe(-4.2)
|
|
21
|
+
expect(round1(-4.25)).toBe(-4.2) // JS rounds toward +∞ for halves
|
|
22
|
+
expect(round1(-4.26)).toBe(-4.3)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('handles zero', () => {
|
|
26
|
+
expect(round1(0)).toBe(0)
|
|
27
|
+
expect(round1(0.04)).toBe(0)
|
|
28
|
+
expect(round1(0.05)).toBe(0.1)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('handles large numbers', () => {
|
|
32
|
+
expect(round1(123456.789)).toBe(123456.8)
|
|
33
|
+
})
|
|
34
|
+
})
|
package/tests/pub.test.ts
CHANGED
|
@@ -27,8 +27,17 @@ const path = '../src/pub'
|
|
|
27
27
|
|
|
28
28
|
describe('publishMessage', () => {
|
|
29
29
|
beforeEach(() => {
|
|
30
|
-
//
|
|
30
|
+
// ensure fresh module state between tests
|
|
31
31
|
jest.resetModules()
|
|
32
|
+
// clear any existing mock instances array if present
|
|
33
|
+
try {
|
|
34
|
+
const mockPubsub = require('@google-cloud/pubsub')
|
|
35
|
+
if (mockPubsub && Array.isArray(mockPubsub.__instances)) {
|
|
36
|
+
mockPubsub.__instances.length = 0
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// ignore if not loaded yet
|
|
40
|
+
}
|
|
32
41
|
})
|
|
33
42
|
|
|
34
43
|
test('publishes and returns messageId, sends JSON buffer', async () => {
|
|
@@ -57,7 +66,7 @@ describe('publishMessage', () => {
|
|
|
57
66
|
await expect(publishMessage('p', 't', undefined as any)).rejects.toThrow('envelope is required')
|
|
58
67
|
})
|
|
59
68
|
|
|
60
|
-
test('caches one PubSub instance
|
|
69
|
+
test('caches one PubSub instance across multiple calls for the same project', async () => {
|
|
61
70
|
const mockPubsub = require('@google-cloud/pubsub')
|
|
62
71
|
const { publishMessage } = require(path)
|
|
63
72
|
|
|
@@ -70,16 +79,49 @@ describe('publishMessage', () => {
|
|
|
70
79
|
expect(publishMock).toHaveBeenCalledTimes(2)
|
|
71
80
|
})
|
|
72
81
|
|
|
73
|
-
test('
|
|
82
|
+
test('reuses the same PubSub instance even when called with a different projectId', async () => {
|
|
74
83
|
const mockPubsub = require('@google-cloud/pubsub')
|
|
75
84
|
const { publishMessage } = require(path)
|
|
76
85
|
|
|
86
|
+
// first call creates instance with project-A
|
|
77
87
|
await publishMessage('project-A', 't1', { x: 1 })
|
|
88
|
+
// second call passes a different project id but module currently reuses the cached instance
|
|
78
89
|
await publishMessage('project-B', 't2', { y: 2 })
|
|
79
90
|
|
|
80
91
|
const instances = mockPubsub.__instances
|
|
81
|
-
|
|
92
|
+
// because src/pub.ts caches a single PubSub instance, only one instance should exist
|
|
93
|
+
expect(instances.length).toBe(1)
|
|
94
|
+
// the created instance was constructed with the first project id
|
|
82
95
|
expect(instances[0].opts).toEqual({ projectId: 'project-A' })
|
|
83
|
-
|
|
96
|
+
|
|
97
|
+
// ensure both publishes were invoked on the same instance (different topics)
|
|
98
|
+
expect(instances[0].topics.get('t1').publishMessage).toHaveBeenCalledTimes(1)
|
|
99
|
+
expect(instances[0].topics.get('t2').publishMessage).toHaveBeenCalledTimes(1)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('logs error and rethrows when publish fails', async () => {
|
|
103
|
+
const mockPubsub = require('@google-cloud/pubsub')
|
|
104
|
+
|
|
105
|
+
// Override the mock PubSub.topic to return a publishMessage that rejects
|
|
106
|
+
mockPubsub.PubSub.prototype.topic = function (name: string) {
|
|
107
|
+
if (!this.topics.has(name)) {
|
|
108
|
+
const publishMessage = jest.fn(() => Promise.reject(new Error('boom')))
|
|
109
|
+
this.topics.set(name, { publishMessage })
|
|
110
|
+
}
|
|
111
|
+
return this.topics.get(name)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const { publishMessage } = require(path)
|
|
115
|
+
|
|
116
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
117
|
+
|
|
118
|
+
await expect(publishMessage('project-err', 'topic-err', { fail: true })).rejects.toThrow('boom')
|
|
119
|
+
|
|
120
|
+
expect(consoleErrorSpy).toHaveBeenCalled()
|
|
121
|
+
const firstArg = consoleErrorSpy.mock.calls[0][0]
|
|
122
|
+
expect(typeof firstArg).toBe('string')
|
|
123
|
+
expect(firstArg).toContain('ERROR: publish failed:')
|
|
124
|
+
|
|
125
|
+
consoleErrorSpy.mockRestore()
|
|
84
126
|
})
|
|
85
127
|
})
|
package/tsconfig.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|