|
| 1 | +/* eslint @typescript-eslint/no-non-null-assertion: 0 */ |
| 2 | +import test from 'ava'; |
| 3 | +import type { Payload } from '@temporalio/common'; |
| 4 | +import { ExternalStorage } from '@temporalio/common/lib/converter/extstore'; |
| 5 | +import { |
| 6 | + isReferencePayload, |
| 7 | + retrieveWorkflowActivation, |
| 8 | + storeWorkflowActivationCompletion, |
| 9 | +} from '@temporalio/common/lib/internal-non-workflow'; |
| 10 | +import { encode } from '@temporalio/common/lib/encoding'; |
| 11 | +import { METADATA_ENCODING_KEY } from '@temporalio/common/lib/converter/types'; |
| 12 | +import { coresdk } from '@temporalio/proto'; |
| 13 | +import { makeFakeDriver } from './extstore-fake-driver'; |
| 14 | + |
| 15 | +/** Build a Payload whose proto-encoded size is at least `bodyBytes`, filled with `fill`. */ |
| 16 | +function makePayload(bodyBytes: number, fill = 0): Payload { |
| 17 | + return { |
| 18 | + metadata: { [METADATA_ENCODING_KEY]: encode('binary/plain') }, |
| 19 | + data: new Uint8Array(bodyBytes).fill(fill), |
| 20 | + }; |
| 21 | +} |
| 22 | + |
| 23 | +function externalStorageWith(driver = makeFakeDriver({ name: 's3' }), payloadSizeThreshold = 96) { |
| 24 | + return { driver, externalStorage: new ExternalStorage({ drivers: [driver], payloadSizeThreshold }) }; |
| 25 | +} |
| 26 | + |
| 27 | +const WORKFLOW_TARGET = { kind: 'workflow', namespace: 'ns', id: 'wf-1', runId: 'run-1' } as const; |
| 28 | + |
| 29 | +test('store offloads a large payload at a singular site and threads the target to the driver', async (t) => { |
| 30 | + const { driver, externalStorage } = externalStorageWith(); |
| 31 | + const completion: coresdk.workflow_completion.IWorkflowActivationCompletion = { |
| 32 | + successful: { commands: [{ completeWorkflowExecution: { result: makePayload(256) } }] }, |
| 33 | + }; |
| 34 | + |
| 35 | + await storeWorkflowActivationCompletion(externalStorage, completion, { target: WORKFLOW_TARGET }); |
| 36 | + |
| 37 | + const result = completion.successful!.commands![0]!.completeWorkflowExecution!.result!; |
| 38 | + t.true(isReferencePayload(result)); |
| 39 | + t.is(driver.storeCalls.length, 1); |
| 40 | + t.deepEqual(driver.storeCalls[0]!.context.target, WORKFLOW_TARGET); |
| 41 | +}); |
| 42 | + |
| 43 | +test('store offloads only above-threshold payloads at a repeated site', async (t) => { |
| 44 | + const { externalStorage } = externalStorageWith(); |
| 45 | + const small = makePayload(0); |
| 46 | + const completion: coresdk.workflow_completion.IWorkflowActivationCompletion = { |
| 47 | + successful: { commands: [{ scheduleActivity: { arguments: [makePayload(256), small] } }] }, |
| 48 | + }; |
| 49 | + |
| 50 | + await storeWorkflowActivationCompletion(externalStorage, completion, { target: WORKFLOW_TARGET }); |
| 51 | + |
| 52 | + const args = completion.successful!.commands![0]!.scheduleActivity!.arguments!; |
| 53 | + t.true(isReferencePayload(args[0]!)); |
| 54 | + t.deepEqual(args[1], small); |
| 55 | +}); |
| 56 | + |
| 57 | +test('store then retrieve round-trips the original payload bytes through a worker message', async (t) => { |
| 58 | + const { driver, externalStorage } = externalStorageWith(); |
| 59 | + const original = makePayload(256, 7); |
| 60 | + const completion: coresdk.workflow_completion.IWorkflowActivationCompletion = { |
| 61 | + successful: { commands: [{ completeWorkflowExecution: { result: original } }] }, |
| 62 | + }; |
| 63 | + |
| 64 | + await storeWorkflowActivationCompletion(externalStorage, completion, { target: WORKFLOW_TARGET }); |
| 65 | + const reference = completion.successful!.commands![0]!.completeWorkflowExecution!.result!; |
| 66 | + t.true(isReferencePayload(reference)); |
| 67 | + |
| 68 | + // The reference produced on the outbound path arrives back on a later inbound activation. |
| 69 | + const activation: coresdk.workflow_activation.IWorkflowActivation = { |
| 70 | + jobs: [{ resolveActivity: { result: { completed: { result: reference } } } }], |
| 71 | + }; |
| 72 | + await retrieveWorkflowActivation(externalStorage, activation); |
| 73 | + |
| 74 | + const retrieved = activation.jobs![0]!.resolveActivity!.result!.completed!.result!; |
| 75 | + t.false(isReferencePayload(retrieved)); |
| 76 | + t.deepEqual(retrieved, original); |
| 77 | + t.is(driver.retrieveCalls.length, 1); |
| 78 | +}); |
0 commit comments