Feat: TO variant creation (#3097)

This commit is contained in:
Carlos R. L. Rodrigues
2023-02-02 09:01:10 -03:00
committed by GitHub
parent a049987215
commit d50db84a33
17 changed files with 853 additions and 459 deletions
@@ -7,6 +7,10 @@ import {
} from "../../transaction"
describe("Transaction Orchestrator", () => {
afterEach(() => {
jest.clearAllMocks()
})
it("Should follow the flow by calling steps in order with the correct payload", async () => {
const mocks = {
one: jest.fn().mockImplementation((payload) => {
@@ -187,7 +191,7 @@ describe("Transaction Orchestrator", () => {
expect(actionOrder).toEqual(["one", "two", "three"])
})
it("Should forward step response if flag 'forwardResponse' is set to true", async () => {
it("Should store invoke's step response if flag 'saveResponse' is set to true", async () => {
const mocks = {
one: jest.fn().mockImplementation((data) => {
return { abc: 1234 }
@@ -195,8 +199,11 @@ describe("Transaction Orchestrator", () => {
two: jest.fn().mockImplementation((data) => {
return { def: "567" }
}),
three: jest.fn().mockImplementation((data) => {
return { end: true }
three: jest.fn().mockImplementation((data, context) => {
return { end: true, onePropAbc: context.invoke.firstMethod.abc }
}),
four: jest.fn().mockImplementation((data) => {
return null
}),
}
@@ -217,24 +224,36 @@ describe("Transaction Orchestrator", () => {
},
},
thirdMethod: {
[TransactionHandlerType.INVOKE]: (data, context) => {
return mocks.three(data, context)
},
},
fourthMethod: {
[TransactionHandlerType.INVOKE]: (data) => {
return mocks.three(data)
return mocks.four(data)
},
},
}
return command[actionId][functionHandlerType]({ ...payload.data })
return command[actionId][functionHandlerType](
payload.data,
payload.context
)
}
const flow: TransactionStepsDefinition = {
next: {
action: "firstMethod",
forwardResponse: true,
saveResponse: true,
next: {
action: "secondMethod",
forwardResponse: true,
saveResponse: true,
next: {
action: "thirdMethod",
saveResponse: true,
next: {
action: "fourthMethod",
},
},
},
},
@@ -245,18 +264,111 @@ describe("Transaction Orchestrator", () => {
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler,
{
prop: 123,
}
{ prop: 123 }
)
await strategy.resume(transaction)
expect(mocks.one).toBeCalledWith({ prop: 123 })
expect(mocks.two).toBeCalledWith({ prop: 123 })
expect(mocks.two).toBeCalledWith({ prop: 123, _response: { abc: 1234 } })
expect(mocks.three).toBeCalledWith(
{ prop: 123 },
{
invoke: {
firstMethod: { abc: 1234 },
secondMethod: { def: "567" },
thirdMethod: { end: true, onePropAbc: 1234 },
},
compensate: {},
}
)
})
expect(mocks.three).toBeCalledWith({ prop: 123, _response: { def: "567" } })
it("Should store compensate's step responses if flag 'saveResponse' is set to true", async () => {
const mocks = {
one: jest.fn().mockImplementation(() => {
return 1
}),
two: jest.fn().mockImplementation(() => {
return 2
}),
compensateOne: jest.fn().mockImplementation((compensateContext) => {
return "compensate 1 - 2 = " + compensateContext.secondMethod.two
}),
compensateTwo: jest.fn().mockImplementation((compensateContext) => {
return { two: "isCompensated" }
}),
}
async function handler(
actionId: string,
functionHandlerType: TransactionHandlerType,
payload: TransactionPayload
) {
const command = {
firstMethod: {
[TransactionHandlerType.INVOKE]: () => {
return mocks.one()
},
[TransactionHandlerType.COMPENSATE]: ({ compensate }) => {
return mocks.compensateOne({ ...compensate })
},
},
secondMethod: {
[TransactionHandlerType.INVOKE]: () => {
return mocks.two()
},
[TransactionHandlerType.COMPENSATE]: ({ compensate }) => {
return mocks.compensateTwo({ ...compensate })
},
},
thirdMethod: {
[TransactionHandlerType.INVOKE]: () => {
throw new Error("failed")
},
},
}
return command[actionId][functionHandlerType](payload.context)
}
const flow: TransactionStepsDefinition = {
next: {
action: "firstMethod",
saveResponse: true,
next: {
action: "secondMethod",
saveResponse: true,
next: {
action: "thirdMethod",
noCompensation: true,
},
},
},
}
const strategy = new TransactionOrchestrator("transaction-name", flow)
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
await strategy.resume(transaction)
const resposes = transaction.getContext()
expect(mocks.compensateTwo).toBeCalledWith({})
expect(mocks.compensateOne).toBeCalledWith({
secondMethod: {
two: "isCompensated",
},
})
expect(resposes.compensate.firstMethod).toEqual(
"compensate 1 - 2 = isCompensated"
)
})
it("Should continue the exection of next steps without waiting for the execution of all its parents when flag 'noWait' is set to true", async () => {
@@ -362,8 +474,10 @@ describe("Transaction Orchestrator", () => {
const flow: TransactionStepsDefinition = {
next: {
action: "firstMethod",
maxRetries: 3,
next: {
action: "secondMethod",
maxRetries: 3,
},
},
}
@@ -379,7 +493,7 @@ describe("Transaction Orchestrator", () => {
expect(transaction.transactionId).toBe("transaction_id_123")
expect(mocks.one).toBeCalledTimes(1)
expect(mocks.two).toBeCalledTimes(1 + strategy.DEFAULT_RETRIES)
expect(mocks.two).toBeCalledTimes(4)
expect(transaction.getState()).toBe(TransactionState.REVERTED)
expect(mocks.compensateOne).toBeCalledTimes(1)
@@ -428,6 +542,7 @@ describe("Transaction Orchestrator", () => {
const flow: TransactionStepsDefinition = {
next: {
action: "firstMethod",
maxRetries: 1,
},
}
@@ -440,7 +555,7 @@ describe("Transaction Orchestrator", () => {
await strategy.resume(transaction)
expect(mocks.one).toBeCalledTimes(1 + strategy.DEFAULT_RETRIES)
expect(mocks.one).toBeCalledTimes(2)
expect(transaction.getState()).toBe(TransactionState.FAILED)
})
@@ -666,4 +781,77 @@ describe("Transaction Orchestrator", () => {
expect(resumedTransaction.getState()).toBe(TransactionState.REVERTED)
})
it("Should revert a transaction when .cancelTransaction() is called", async () => {
const mocks = {
one: jest.fn().mockImplementation((payload) => {
return payload
}),
oneCompensate: jest.fn().mockImplementation((payload) => {
return payload
}),
two: jest.fn().mockImplementation((payload) => {
return payload
}),
twoCompensate: jest.fn().mockImplementation((payload) => {
return payload
}),
}
async function handler(
actionId: string,
functionHandlerType: TransactionHandlerType,
payload: TransactionPayload
) {
const command = {
firstMethod: {
[TransactionHandlerType.INVOKE]: () => {
mocks.one(payload)
},
[TransactionHandlerType.COMPENSATE]: () => {
mocks.oneCompensate(payload)
},
},
secondMethod: {
[TransactionHandlerType.INVOKE]: () => {
mocks.two(payload)
},
[TransactionHandlerType.COMPENSATE]: () => {
mocks.twoCompensate(payload)
},
},
}
return command[actionId][functionHandlerType](payload)
}
const flow: TransactionStepsDefinition = {
next: {
action: "firstMethod",
next: {
action: "secondMethod",
},
},
}
const strategy = new TransactionOrchestrator("transaction-name", flow)
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
await strategy.resume(transaction)
expect(transaction.getState()).toBe(TransactionState.DONE)
expect(mocks.one).toBeCalledTimes(1)
expect(mocks.two).toBeCalledTimes(1)
await strategy.cancelTransaction(transaction)
expect(transaction.getState()).toBe(TransactionState.REVERTED)
expect(mocks.one).toBeCalledTimes(1)
expect(mocks.two).toBeCalledTimes(1)
expect(mocks.oneCompensate).toBeCalledTimes(1)
expect(mocks.twoCompensate).toBeCalledTimes(1)
})
})