@goatlab/tasks-adapter-gcp 0.4.9 → 0.4.10
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/cloudtask.spec.js +145 -137
- package/dist/cloudtask.spec.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
package/dist/cloudtask.spec.js
CHANGED
|
@@ -7,151 +7,159 @@ const vitest_1 = require("vitest");
|
|
|
7
7
|
const CloudTaskConnector_js_1 = require("./CloudTaskConnector.js");
|
|
8
8
|
// Parse service account from env
|
|
9
9
|
const serviceAccountBase64 = process.env.FIREBASE_SERVICE_ACCOUNT;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Note: GCP Cloud Tasks uses HTTP callbacks, not workers.
|
|
26
|
-
// For lifecycle tests, tasks will fail because there's no real HTTP endpoint.
|
|
27
|
-
// We skip lifecycle tests since GCP doesn't have a startWorker concept.
|
|
28
|
-
(0, test_suite_1.taskConnectorTestSuite)({ describe: vitest_1.describe, it: vitest_1.it, expect: vitest_1.expect, beforeAll: vitest_1.beforeAll, afterAll: vitest_1.afterAll, beforeEach: vitest_1.beforeEach, afterEach: vitest_1.afterEach }, () => cloudTaskConnector, {
|
|
29
|
-
taskCompletionTimeout: 30000,
|
|
30
|
-
statusCheckInterval: 2000,
|
|
31
|
-
// Skip lifecycle tests - GCP Cloud Tasks doesn't use workers,
|
|
32
|
-
// it uses HTTP callbacks. Without a real HTTP server, tasks will fail.
|
|
33
|
-
skipLifecycleTests: true,
|
|
34
|
-
// No startWorker for GCP - it's HTTP callback based
|
|
35
|
-
startWorker: undefined,
|
|
36
|
-
});
|
|
37
|
-
// GCP-specific tests for the HTTP callback model
|
|
38
|
-
(0, vitest_1.describe)('CloudTaskConnector GCP-Specific Tests', () => {
|
|
39
|
-
(0, vitest_1.it)('should queue a task and get its status', async () => {
|
|
40
|
-
class TestTask extends tasks_core_1.ShouldQueue {
|
|
41
|
-
postUrl = 'https://httpbin.org/post';
|
|
42
|
-
taskName = 'gcp_test_task';
|
|
43
|
-
constructor() {
|
|
44
|
-
super({ connector: cloudTaskConnector });
|
|
45
|
-
}
|
|
46
|
-
async handle() {
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const task = new TestTask();
|
|
51
|
-
const status = await task.queue({ text: 'Hello GCP!' });
|
|
52
|
-
(0, vitest_1.expect)(status).toHaveProperty('id');
|
|
53
|
-
(0, vitest_1.expect)(status).toHaveProperty('name');
|
|
54
|
-
(0, vitest_1.expect)(status).toHaveProperty('status', 'QUEUED');
|
|
55
|
-
(0, vitest_1.expect)(status).toHaveProperty('attempts', 0);
|
|
56
|
-
(0, vitest_1.expect)(status.name).toContain('gcp_test_task');
|
|
57
|
-
// Get status - task may have already been dispatched or completed
|
|
58
|
-
// GCP Cloud Tasks removes completed tasks, so payload may be empty if task completed quickly
|
|
59
|
-
const fullStatus = await task.getStatus(status.id);
|
|
60
|
-
(0, vitest_1.expect)(fullStatus).toHaveProperty('id');
|
|
61
|
-
// If task still exists, check payload; if completed/removed, payload will be empty
|
|
62
|
-
if (fullStatus.status !== 'COMPLETED' ||
|
|
63
|
-
Object.keys(fullStatus.payload).length > 0) {
|
|
64
|
-
(0, vitest_1.expect)(fullStatus.payload).toHaveProperty('text', 'Hello GCP!');
|
|
65
|
-
}
|
|
10
|
+
const hasCredentials = !!serviceAccountBase64;
|
|
11
|
+
const gcpServiceAccount = hasCredentials
|
|
12
|
+
? JSON.parse(Buffer.from(serviceAccountBase64, 'base64').toString('utf8'))
|
|
13
|
+
: undefined;
|
|
14
|
+
// Skip all tests when credentials are not available
|
|
15
|
+
const describeWithCredentials = hasCredentials ? vitest_1.describe : vitest_1.describe.skip;
|
|
16
|
+
describeWithCredentials('CloudTaskConnector (requires GCP credentials)', () => {
|
|
17
|
+
// Create real GCP Cloud Tasks connector
|
|
18
|
+
const cloudTaskConnector = new CloudTaskConnector_js_1.CloudTaskConnector({
|
|
19
|
+
gcpServiceAccount,
|
|
20
|
+
location: 'europe-west1',
|
|
21
|
+
encryptionKey: 'test-encryption-key-32chars!!!!',
|
|
22
|
+
gcpProject: gcpServiceAccount?.project_id,
|
|
23
|
+
// Enable payload cache for testing - GCP removes completed tasks immediately
|
|
24
|
+
enablePayloadCache: true,
|
|
66
25
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
26
|
+
// Run the standardized test suite
|
|
27
|
+
// Note: GCP Cloud Tasks uses HTTP callbacks, not workers.
|
|
28
|
+
// For lifecycle tests, tasks will fail because there's no real HTTP endpoint.
|
|
29
|
+
// We skip lifecycle tests since GCP doesn't have a startWorker concept.
|
|
30
|
+
(0, test_suite_1.taskConnectorTestSuite)({ describe: vitest_1.describe, it: vitest_1.it, expect: vitest_1.expect, beforeAll: vitest_1.beforeAll, afterAll: vitest_1.afterAll, beforeEach: vitest_1.beforeEach, afterEach: vitest_1.afterEach }, () => cloudTaskConnector, {
|
|
31
|
+
taskCompletionTimeout: 30000,
|
|
32
|
+
statusCheckInterval: 2000,
|
|
33
|
+
// Skip lifecycle tests - GCP Cloud Tasks doesn't use workers,
|
|
34
|
+
// it uses HTTP callbacks. Without a real HTTP server, tasks will fail.
|
|
35
|
+
skipLifecycleTests: true,
|
|
36
|
+
// No startWorker for GCP - it's HTTP callback based
|
|
37
|
+
startWorker: undefined,
|
|
38
|
+
});
|
|
39
|
+
// GCP-specific tests for the HTTP callback model
|
|
40
|
+
(0, vitest_1.describe)('CloudTaskConnector GCP-Specific Tests', () => {
|
|
41
|
+
(0, vitest_1.it)('should queue a task and get its status', async () => {
|
|
42
|
+
class TestTask extends tasks_core_1.ShouldQueue {
|
|
43
|
+
postUrl = 'https://httpbin.org/post';
|
|
44
|
+
taskName = 'gcp_test_task';
|
|
45
|
+
constructor() {
|
|
46
|
+
super({ connector: cloudTaskConnector });
|
|
47
|
+
}
|
|
48
|
+
async handle() {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
74
51
|
}
|
|
75
|
-
|
|
76
|
-
|
|
52
|
+
const task = new TestTask();
|
|
53
|
+
const status = await task.queue({ text: 'Hello GCP!' });
|
|
54
|
+
(0, vitest_1.expect)(status).toHaveProperty('id');
|
|
55
|
+
(0, vitest_1.expect)(status).toHaveProperty('name');
|
|
56
|
+
(0, vitest_1.expect)(status).toHaveProperty('status', 'QUEUED');
|
|
57
|
+
(0, vitest_1.expect)(status).toHaveProperty('attempts', 0);
|
|
58
|
+
(0, vitest_1.expect)(status.name).toContain('gcp_test_task');
|
|
59
|
+
// Get status - task may have already been dispatched or completed
|
|
60
|
+
// GCP Cloud Tasks removes completed tasks, so payload may be empty if task completed quickly
|
|
61
|
+
const fullStatus = await task.getStatus(status.id);
|
|
62
|
+
(0, vitest_1.expect)(fullStatus).toHaveProperty('id');
|
|
63
|
+
// If task still exists, check payload; if completed/removed, payload will be empty
|
|
64
|
+
if (fullStatus.status !== 'COMPLETED' ||
|
|
65
|
+
Object.keys(fullStatus.payload).length > 0) {
|
|
66
|
+
(0, vitest_1.expect)(fullStatus.payload).toHaveProperty('text', 'Hello GCP!');
|
|
77
67
|
}
|
|
78
|
-
}
|
|
79
|
-
const task = new UnreachableTask();
|
|
80
|
-
const status = await task.queue({ text: 'This endpoint is unreachable' });
|
|
81
|
-
(0, vitest_1.expect)(status).toHaveProperty('id');
|
|
82
|
-
(0, vitest_1.expect)(status.status).toBe('QUEUED');
|
|
83
|
-
// The task will eventually fail since the endpoint is unreachable
|
|
84
|
-
// We just verify we can queue and get status
|
|
85
|
-
const fullStatus = await task.getStatus(status.id);
|
|
86
|
-
(0, vitest_1.expect)(fullStatus).toHaveProperty('payload');
|
|
87
|
-
});
|
|
88
|
-
(0, vitest_1.it)('should encrypt task body to base64 string', () => {
|
|
89
|
-
const original = { text: 'secret data', nested: { value: 123 } };
|
|
90
|
-
// encryptBody expects an object with 'content' key containing stringified data
|
|
91
|
-
const encrypted = cloudTaskConnector.encryptBody({
|
|
92
|
-
content: JSON.stringify(original),
|
|
93
68
|
});
|
|
94
|
-
(0, vitest_1.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
69
|
+
(0, vitest_1.it)('should queue task with valid but unreachable endpoint', async () => {
|
|
70
|
+
class UnreachableTask extends tasks_core_1.ShouldQueue {
|
|
71
|
+
// Use a valid URL that will timeout/fail when GCP tries to call it
|
|
72
|
+
postUrl = 'https://example.invalid/webhook';
|
|
73
|
+
taskName = 'gcp_unreachable_task';
|
|
74
|
+
constructor() {
|
|
75
|
+
super({ connector: cloudTaskConnector });
|
|
76
|
+
}
|
|
77
|
+
async handle() {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const task = new UnreachableTask();
|
|
82
|
+
const status = await task.queue({ text: 'This endpoint is unreachable' });
|
|
83
|
+
(0, vitest_1.expect)(status).toHaveProperty('id');
|
|
84
|
+
(0, vitest_1.expect)(status.status).toBe('QUEUED');
|
|
85
|
+
// The task will eventually fail since the endpoint is unreachable
|
|
86
|
+
// We just verify we can queue and get status
|
|
87
|
+
const fullStatus = await task.getStatus(status.id);
|
|
88
|
+
(0, vitest_1.expect)(fullStatus).toHaveProperty('payload');
|
|
89
|
+
});
|
|
90
|
+
(0, vitest_1.it)('should encrypt task body to base64 string', () => {
|
|
91
|
+
const original = { text: 'secret data', nested: { value: 123 } };
|
|
92
|
+
// encryptBody expects an object with 'content' key containing stringified data
|
|
93
|
+
const encrypted = cloudTaskConnector.encryptBody({
|
|
94
|
+
content: JSON.stringify(original),
|
|
95
|
+
});
|
|
96
|
+
(0, vitest_1.expect)(encrypted).toBeDefined();
|
|
97
|
+
(0, vitest_1.expect)(typeof encrypted === 'string').toBe(true);
|
|
98
|
+
// Should be base64 encoded
|
|
99
|
+
(0, vitest_1.expect)(() => Buffer.from(encrypted, 'base64')).not.toThrow();
|
|
100
|
+
});
|
|
101
|
+
(0, vitest_1.it)('should expose tenantId when set', () => {
|
|
102
|
+
const tenantConnector = new CloudTaskConnector_js_1.CloudTaskConnector({
|
|
103
|
+
gcpServiceAccount,
|
|
104
|
+
location: 'europe-west1',
|
|
105
|
+
encryptionKey: 'test-encryption-key-32chars!!!!',
|
|
106
|
+
gcpProject: gcpServiceAccount.project_id,
|
|
107
|
+
tenantId: 'test-tenant',
|
|
108
|
+
});
|
|
109
|
+
(0, vitest_1.expect)(tenantConnector.tenantId).toBe('test-tenant');
|
|
110
|
+
});
|
|
111
|
+
(0, vitest_1.it)('should have undefined tenantId when not set', () => {
|
|
112
|
+
(0, vitest_1.expect)(cloudTaskConnector.tenantId).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
(0, vitest_1.it)('forTenant() should create a new connector with tenant prefix', () => {
|
|
115
|
+
const tenantConnector = cloudTaskConnector.forTenant('acme-corp');
|
|
116
|
+
(0, vitest_1.expect)(tenantConnector.tenantId).toBe('acme-corp');
|
|
117
|
+
// Original connector should be unchanged
|
|
118
|
+
(0, vitest_1.expect)(cloudTaskConnector.tenantId).toBeUndefined();
|
|
106
119
|
});
|
|
107
|
-
(0, vitest_1.expect)(tenantConnector.tenantId).toBe('test-tenant');
|
|
108
120
|
});
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
// Multi-tenant isolation tests using task name prefixes
|
|
122
|
+
// Note: GCP Cloud Tasks uses HTTP callbacks (push model), not workers (pull model).
|
|
123
|
+
// The multi-tenant tests verify task isolation and basic API operations.
|
|
124
|
+
const baseConnector = new CloudTaskConnector_js_1.CloudTaskConnector({
|
|
125
|
+
gcpServiceAccount,
|
|
126
|
+
location: 'europe-west1',
|
|
127
|
+
encryptionKey: 'test-encryption-key-32chars!!!!',
|
|
128
|
+
gcpProject: gcpServiceAccount?.project_id,
|
|
129
|
+
// Enable payload cache for testing - GCP removes completed tasks immediately
|
|
130
|
+
enablePayloadCache: true,
|
|
111
131
|
});
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
// Store tenant connectors for cleanup
|
|
133
|
+
const tenantConnectors = new Map();
|
|
134
|
+
(0, test_suite_1.multiTenantTestSuite)({ describe: vitest_1.describe, it: vitest_1.it, expect: vitest_1.expect, beforeAll: vitest_1.beforeAll, afterAll: vitest_1.afterAll, beforeEach: vitest_1.beforeEach, afterEach: vitest_1.afterEach }, {
|
|
135
|
+
createTenantConnector: tenantId => {
|
|
136
|
+
const tenantConnector = baseConnector.forTenant(tenantId);
|
|
137
|
+
tenantConnectors.set(tenantId, tenantConnector);
|
|
138
|
+
return tenantConnector;
|
|
139
|
+
},
|
|
140
|
+
// GCP Cloud Tasks uses HTTP callbacks, not workers.
|
|
141
|
+
// We provide a no-op startWorker since GCP pushes to the HTTP endpoint.
|
|
142
|
+
startTenantWorker: async (_tenantId, _tasks) => {
|
|
143
|
+
// GCP Cloud Tasks is push-based (HTTP callbacks), not pull-based (workers)
|
|
144
|
+
// The task completes when GCP receives a 2xx response from the endpoint
|
|
145
|
+
return async () => {
|
|
146
|
+
// No cleanup needed - GCP handles queue management
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
taskCompletionTimeout: 30000,
|
|
150
|
+
statusCheckInterval: 2000,
|
|
151
|
+
workerStartupDelay: 1000,
|
|
152
|
+
supportsForTenant: true,
|
|
153
|
+
// Use a real HTTP endpoint that GCP can reach and will return 200 OK
|
|
154
|
+
testPostUrl: 'https://httpbin.org/post',
|
|
155
|
+
// Skip data isolation tests - GCP tasks are identified by full path which
|
|
156
|
+
// includes the task name, so cross-tenant getStatus would require different logic
|
|
157
|
+
runIsolationTests: false,
|
|
117
158
|
});
|
|
118
159
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
gcpServiceAccount,
|
|
124
|
-
location: 'europe-west1',
|
|
125
|
-
encryptionKey: 'test-encryption-key-32chars!!!!',
|
|
126
|
-
gcpProject: gcpServiceAccount.project_id,
|
|
127
|
-
// Enable payload cache for testing - GCP removes completed tasks immediately
|
|
128
|
-
enablePayloadCache: true,
|
|
129
|
-
});
|
|
130
|
-
// Store tenant connectors for cleanup
|
|
131
|
-
const tenantConnectors = new Map();
|
|
132
|
-
(0, test_suite_1.multiTenantTestSuite)({ describe: vitest_1.describe, it: vitest_1.it, expect: vitest_1.expect, beforeAll: vitest_1.beforeAll, afterAll: vitest_1.afterAll, beforeEach: vitest_1.beforeEach, afterEach: vitest_1.afterEach }, {
|
|
133
|
-
createTenantConnector: tenantId => {
|
|
134
|
-
const tenantConnector = baseConnector.forTenant(tenantId);
|
|
135
|
-
tenantConnectors.set(tenantId, tenantConnector);
|
|
136
|
-
return tenantConnector;
|
|
137
|
-
},
|
|
138
|
-
// GCP Cloud Tasks uses HTTP callbacks, not workers.
|
|
139
|
-
// We provide a no-op startWorker since GCP pushes to the HTTP endpoint.
|
|
140
|
-
startTenantWorker: async (_tenantId, _tasks) => {
|
|
141
|
-
// GCP Cloud Tasks is push-based (HTTP callbacks), not pull-based (workers)
|
|
142
|
-
// The task completes when GCP receives a 2xx response from the endpoint
|
|
143
|
-
return async () => {
|
|
144
|
-
// No cleanup needed - GCP handles queue management
|
|
145
|
-
};
|
|
146
|
-
},
|
|
147
|
-
taskCompletionTimeout: 30000,
|
|
148
|
-
statusCheckInterval: 2000,
|
|
149
|
-
workerStartupDelay: 1000,
|
|
150
|
-
supportsForTenant: true,
|
|
151
|
-
// Use a real HTTP endpoint that GCP can reach and will return 200 OK
|
|
152
|
-
testPostUrl: 'https://httpbin.org/post',
|
|
153
|
-
// Skip data isolation tests - GCP tasks are identified by full path which
|
|
154
|
-
// includes the task name, so cross-tenant getStatus would require different logic
|
|
155
|
-
runIsolationTests: false,
|
|
160
|
+
(0, vitest_1.describe)('CloudTaskConnector (no credentials)', () => {
|
|
161
|
+
vitest_1.it.skipIf(hasCredentials)('skipped: FIREBASE_SERVICE_ACCOUNT not set', () => {
|
|
162
|
+
// This test exists to provide a visible skip message when credentials are unavailable
|
|
163
|
+
});
|
|
156
164
|
});
|
|
157
165
|
//# sourceMappingURL=cloudtask.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudtask.spec.js","sourceRoot":"","sources":["../src/cloudtask.spec.ts"],"names":[],"mappings":";AAAA,yCAAyC;;AAEzC,oDAAiD;AACjD,+DAGuC;AACvC,mCAQe;AACf,mEAA4D;AAE5D,iCAAiC;AACjC,MAAM,oBAAoB,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAA;AAEjE,
|
|
1
|
+
{"version":3,"file":"cloudtask.spec.js","sourceRoot":"","sources":["../src/cloudtask.spec.ts"],"names":[],"mappings":"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"}
|