228 lines
6.7 KiB
TypeScript
228 lines
6.7 KiB
TypeScript
import { afterEach, describe, it, expect, jest } from "@jest/globals";
|
|
import {
|
|
PollSolanaTransactions,
|
|
PollSolanaTransactionsConfig,
|
|
PollSolanaTransactionsMetadata,
|
|
} from "../../../../src/domain/actions";
|
|
import {
|
|
MetadataRepository,
|
|
SolanaSlotRepository,
|
|
StatRepository,
|
|
} from "../../../../src/domain/repositories";
|
|
import { thenWaitForAssertion } from "../../../wait-assertion";
|
|
import { solana } from "../../../../src/domain/entities";
|
|
import { Fallible } from "../../../../src/domain/errors";
|
|
|
|
let pollSolanaTransactions: PollSolanaTransactions;
|
|
let cfg: PollSolanaTransactionsConfig;
|
|
|
|
let metadataRepo: MetadataRepository<PollSolanaTransactionsMetadata>;
|
|
let metadataGetSpy: jest.SpiedFunction<MetadataRepository<any>["get"]>;
|
|
let metadataSaveSpy: jest.SpiedFunction<MetadataRepository<any>["save"]>;
|
|
let solanaSlotRepo: SolanaSlotRepository;
|
|
let getLatestSlotSpy: jest.SpiedFunction<SolanaSlotRepository["getLatestSlot"]>;
|
|
let getSigsSpy: jest.SpiedFunction<SolanaSlotRepository["getSignaturesForAddress"]>;
|
|
let getBlockSpy: jest.SpiedFunction<SolanaSlotRepository["getBlock"]>;
|
|
let handlers = {
|
|
working: (logs: any[]) => Promise.resolve(),
|
|
};
|
|
let handlerSpy: jest.SpiedFunction<(transactions: any[]) => Promise<void>>;
|
|
let statsRepo: StatRepository;
|
|
|
|
describe("PollSolanaTransactions", () => {
|
|
afterEach(async () => {
|
|
await pollSolanaTransactions.stop();
|
|
});
|
|
|
|
it("should be able to read transactions from latest slot when no fromSlot is configured", async () => {
|
|
const currentSlot = 10;
|
|
const expectedSigs = givenSigs();
|
|
const expectedTxs = givenTxs();
|
|
|
|
givenCfg();
|
|
givenStatsRepository();
|
|
givenMetadataRepository();
|
|
givenSolanaSlotRepository(currentSlot, givenBlock(1), expectedSigs, expectedTxs);
|
|
givenPollSolanaTransactions();
|
|
|
|
pollSolanaTransactions.run([handlers.working]);
|
|
|
|
await thenWaitForAssertion(
|
|
() => expect(metadataGetSpy).toHaveBeenCalledWith(cfg.id),
|
|
() => expect(getLatestSlotSpy).toHaveBeenCalledWith(cfg.commitment),
|
|
() => expect(getBlockSpy).toHaveBeenCalledWith(currentSlot, cfg.commitment),
|
|
() => expect(handlerSpy).toHaveBeenCalledWith(expectedTxs),
|
|
() =>
|
|
expect(getSigsSpy).toHaveBeenCalledWith(
|
|
cfg.programId,
|
|
expectedSigs[0].signature,
|
|
expectedSigs[expectedSigs.length - 1].signature,
|
|
cfg.signaturesLimit,
|
|
"confirmed"
|
|
),
|
|
() =>
|
|
expect(metadataSaveSpy).toHaveBeenCalledWith(cfg.id, {
|
|
lastSlot: currentSlot,
|
|
})
|
|
);
|
|
});
|
|
|
|
it("should be able to read transactions from last known slot", async () => {
|
|
const latestSlot = 100;
|
|
const lastSlot = 10;
|
|
const expectedSigs = givenSigs();
|
|
const expectedTxs = givenTxs();
|
|
|
|
givenCfg();
|
|
givenStatsRepository();
|
|
givenMetadataRepository({ lastSlot });
|
|
givenSolanaSlotRepository(latestSlot, givenBlock(1), expectedSigs, expectedTxs);
|
|
givenPollSolanaTransactions();
|
|
|
|
pollSolanaTransactions.run([handlers.working]);
|
|
|
|
await thenWaitForAssertion(
|
|
() => expect(getBlockSpy).toHaveBeenCalledWith(lastSlot + 1, cfg.commitment),
|
|
() => expect(handlerSpy).toHaveBeenCalledWith(expectedTxs),
|
|
() =>
|
|
expect(getSigsSpy).toHaveBeenCalledWith(
|
|
cfg.programId,
|
|
expectedSigs[0].signature,
|
|
expectedSigs[expectedSigs.length - 1].signature,
|
|
cfg.signaturesLimit,
|
|
"confirmed"
|
|
),
|
|
() =>
|
|
expect(metadataSaveSpy).toHaveBeenCalledWith(cfg.id, {
|
|
lastSlot: latestSlot,
|
|
})
|
|
);
|
|
});
|
|
|
|
it("should be able to read transactions from last known slot and stop when toSlot is reached", async () => {
|
|
const latestSlot = 100;
|
|
const lastSlot = 10;
|
|
const toSlot = 50;
|
|
const expectedSigs = givenSigs();
|
|
const expectedTxs = givenTxs();
|
|
|
|
givenCfg({ toSlot });
|
|
givenStatsRepository();
|
|
givenMetadataRepository({ lastSlot });
|
|
givenSolanaSlotRepository(latestSlot, givenBlock(1), expectedSigs, expectedTxs);
|
|
givenPollSolanaTransactions();
|
|
|
|
pollSolanaTransactions.run([handlers.working]);
|
|
|
|
await thenWaitForAssertion(
|
|
() =>
|
|
expect(metadataSaveSpy).toHaveBeenCalledWith(cfg.id, {
|
|
lastSlot: toSlot,
|
|
}),
|
|
() => expect(pollSolanaTransactions.hasNext()).resolves.toBe(false)
|
|
);
|
|
});
|
|
});
|
|
|
|
const givenCfg = (overrides?: Partial<PollSolanaTransactionsConfig>) => {
|
|
cfg = new PollSolanaTransactionsConfig("anId", "programID", "confirmed");
|
|
Object.assign(cfg, overrides);
|
|
};
|
|
|
|
const givenMetadataRepository = (data?: PollSolanaTransactionsMetadata) => {
|
|
metadataRepo = {
|
|
get: () => Promise.resolve(data),
|
|
save: () => Promise.resolve(),
|
|
};
|
|
metadataGetSpy = jest.spyOn(metadataRepo, "get");
|
|
metadataSaveSpy = jest.spyOn(metadataRepo, "save");
|
|
};
|
|
|
|
const givenBlock = (blockTime: number) => {
|
|
return {
|
|
blockhash: "aBlockHash",
|
|
transactions: [
|
|
{
|
|
transaction: {
|
|
message: {
|
|
accountKeys: [],
|
|
compiledInstructions: [
|
|
{
|
|
programIdIndex: 0,
|
|
accounts: [],
|
|
data: "",
|
|
},
|
|
],
|
|
instructions: [
|
|
{
|
|
programIdIndex: 0,
|
|
accounts: [],
|
|
data: "",
|
|
},
|
|
],
|
|
},
|
|
signatures: ["aSignature"],
|
|
},
|
|
},
|
|
],
|
|
blockTime,
|
|
} as any as solana.Block;
|
|
};
|
|
|
|
const givenSigs = () => {
|
|
return [
|
|
{
|
|
signature: "aSignature",
|
|
err: null,
|
|
blockTime: 1,
|
|
},
|
|
];
|
|
};
|
|
|
|
const givenTxs = () => {
|
|
return [
|
|
{
|
|
signatures: ["aSignature"],
|
|
message: {
|
|
instructions: [
|
|
{
|
|
programIdIndex: 0,
|
|
accounts: [],
|
|
data: "",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
] as any as solana.Transaction[];
|
|
};
|
|
|
|
const givenSolanaSlotRepository = (
|
|
currentSlot: number,
|
|
block: solana.Block = {} as any as solana.Block,
|
|
sigs: solana.ConfirmedSignatureInfo[] = [],
|
|
txs: solana.Transaction[] = []
|
|
) => {
|
|
solanaSlotRepo = {
|
|
getLatestSlot: () => Promise.resolve(currentSlot),
|
|
getBlock: () => Promise.resolve(Fallible.ok(block)),
|
|
getSignaturesForAddress: () => Promise.resolve(sigs),
|
|
getTransactions: () => Promise.resolve(txs),
|
|
};
|
|
getBlockSpy = jest.spyOn(solanaSlotRepo, "getBlock");
|
|
getLatestSlotSpy = jest.spyOn(solanaSlotRepo, "getLatestSlot");
|
|
getSigsSpy = jest.spyOn(solanaSlotRepo, "getSignaturesForAddress");
|
|
};
|
|
|
|
const givenPollSolanaTransactions = () => {
|
|
handlerSpy = jest.spyOn(handlers, "working");
|
|
pollSolanaTransactions = new PollSolanaTransactions(metadataRepo, solanaSlotRepo, statsRepo, cfg);
|
|
};
|
|
|
|
const givenStatsRepository = () => {
|
|
statsRepo = {
|
|
count: () => {},
|
|
measure: () => {},
|
|
report: () => Promise.resolve(""),
|
|
};
|
|
};
|