@moonwall/cli
Version:
Testing framework for the Moon family of projects
266 lines (263 loc) • 10.3 kB
JavaScript
// src/internal/effect/__tests__/ChopsticksService.test.ts
import { describe, it, expect } from "vitest";
import { Effect as Effect2, Layer } from "effect";
import { BuildBlockMode } from "@acala-network/chopsticks";
// src/internal/effect/ChopsticksService.ts
import { Context, Data } from "effect";
var ChopsticksSetupError = class extends Data.TaggedError("ChopsticksSetupError") {
};
var ChopsticksBlockError = class extends Data.TaggedError("ChopsticksBlockError") {
};
var ChopsticksStorageError = class extends Data.TaggedError("ChopsticksStorageError") {
};
var ChopsticksExtrinsicError = class extends Data.TaggedError("ChopsticksExtrinsicError") {
};
var ChopsticksXcmError = class extends Data.TaggedError("ChopsticksXcmError") {
};
var ChopsticksCleanupError = class extends Data.TaggedError("ChopsticksCleanupError") {
};
var ChopsticksService = class extends Context.Tag("ChopsticksService")() {
};
var ChopsticksConfigTag = class extends Context.Tag("ChopsticksConfig")() {
};
// src/internal/effect/__tests__/ChopsticksService.test.ts
describe("ChopsticksService - Phase 1: Types and Errors", () => {
describe("Tagged Error Types", () => {
it("should create ChopsticksSetupError with correct tag and properties", () => {
const error = new ChopsticksSetupError({
cause: new Error("Connection failed"),
endpoint: "wss://rpc.polkadot.io",
block: 12345
});
expect(error._tag).toBe("ChopsticksSetupError");
expect(error.endpoint).toBe("wss://rpc.polkadot.io");
expect(error.block).toBe(12345);
expect(error.cause).toBeInstanceOf(Error);
});
it("should create ChopsticksBlockError with correct tag and properties", () => {
const error = new ChopsticksBlockError({
cause: new Error("Block creation failed"),
operation: "newBlock",
blockIdentifier: 100
});
expect(error._tag).toBe("ChopsticksBlockError");
expect(error.operation).toBe("newBlock");
expect(error.blockIdentifier).toBe(100);
});
it("should create ChopsticksStorageError with correct tag and properties", () => {
const error = new ChopsticksStorageError({
cause: new Error("Storage write failed"),
module: "System",
method: "Account"
});
expect(error._tag).toBe("ChopsticksStorageError");
expect(error.module).toBe("System");
expect(error.method).toBe("Account");
});
it("should create ChopsticksExtrinsicError with correct tag and properties", () => {
const error = new ChopsticksExtrinsicError({
cause: new Error("Extrinsic validation failed"),
operation: "validate",
extrinsic: "0x1234"
});
expect(error._tag).toBe("ChopsticksExtrinsicError");
expect(error.operation).toBe("validate");
expect(error.extrinsic).toBe("0x1234");
});
it("should create ChopsticksXcmError with correct tag and properties", () => {
const error = new ChopsticksXcmError({
cause: new Error("UMP send failed"),
messageType: "ump",
paraId: 2e3
});
expect(error._tag).toBe("ChopsticksXcmError");
expect(error.messageType).toBe("ump");
expect(error.paraId).toBe(2e3);
});
it("should create ChopsticksCleanupError with correct tag", () => {
const error = new ChopsticksCleanupError({
cause: new Error("Cleanup failed")
});
expect(error._tag).toBe("ChopsticksCleanupError");
});
});
describe("Error Pattern Matching with catchTag", () => {
it("should allow pattern matching on ChopsticksSetupError", async () => {
const program = Effect2.gen(function* () {
yield* Effect2.fail(
new ChopsticksSetupError({
cause: new Error("test"),
endpoint: "wss://test.io"
})
);
return "success";
}).pipe(
Effect2.catchTag(
"ChopsticksSetupError",
(error) => Effect2.succeed(`Caught setup error for ${error.endpoint}`)
)
);
const result = await Effect2.runPromise(program);
expect(result).toBe("Caught setup error for wss://test.io");
});
it("should allow pattern matching on ChopsticksBlockError", async () => {
const program = Effect2.gen(function* () {
yield* Effect2.fail(
new ChopsticksBlockError({
cause: new Error("test"),
operation: "setHead",
blockIdentifier: "0xabc"
})
);
return "success";
}).pipe(
Effect2.catchTag(
"ChopsticksBlockError",
(error) => Effect2.succeed(`Caught ${error.operation} error`)
)
);
const result = await Effect2.runPromise(program);
expect(result).toBe("Caught setHead error");
});
it("should allow catching multiple error types with catchTags", async () => {
const failWithSetup = Effect2.fail(
new ChopsticksSetupError({ cause: new Error("setup"), endpoint: "wss://test" })
);
const failWithBlock = Effect2.fail(
new ChopsticksBlockError({ cause: new Error("block"), operation: "newBlock" })
);
const handleErrors = (effect) => effect.pipe(
Effect2.catchTags({
ChopsticksSetupError: (e) => Effect2.succeed(`setup: ${e.endpoint}`),
ChopsticksBlockError: (e) => Effect2.succeed(`block: ${e.operation}`)
})
);
const result1 = await Effect2.runPromise(handleErrors(failWithSetup));
expect(result1).toBe("setup: wss://test");
const result2 = await Effect2.runPromise(handleErrors(failWithBlock));
expect(result2).toBe("block: newBlock");
});
});
describe("Service Tag Definition", () => {
it("should have ChopsticksService tag with correct identifier", () => {
expect(ChopsticksService).toBeDefined();
expect(ChopsticksService.key).toBe("ChopsticksService");
});
it("should have ChopsticksConfigTag with correct identifier", () => {
expect(ChopsticksConfigTag).toBeDefined();
expect(ChopsticksConfigTag.key).toBe("ChopsticksConfig");
});
it("should require ChopsticksService in Effect context", () => {
const program = Effect2.gen(function* () {
const service = yield* ChopsticksService;
return service.addr;
});
expect(ChopsticksService.key).toBe("ChopsticksService");
});
it("should allow providing a mock ChopsticksService", async () => {
const mockService = {
chain: {},
addr: "127.0.0.1:8000",
port: 8e3,
createBlock: () => Effect2.succeed({ block: { hash: "0x123", number: 1 } }),
setStorage: () => Effect2.void,
submitExtrinsic: () => Effect2.succeed("0xhash"),
dryRunExtrinsic: () => Effect2.succeed({ success: true, storageDiff: [] }),
getBlock: () => Effect2.succeed({ hash: "0x123", number: 1 }),
setHead: () => Effect2.void,
submitUpwardMessages: () => Effect2.void,
submitDownwardMessages: () => Effect2.void,
submitHorizontalMessages: () => Effect2.void
};
const mockLayer = Layer.succeed(ChopsticksService, mockService);
const program = Effect2.gen(function* () {
const service = yield* ChopsticksService;
return service.addr;
}).pipe(Effect2.provide(mockLayer));
const result = await Effect2.runPromise(program);
expect(result).toBe("127.0.0.1:8000");
});
});
describe("Config Type Validation", () => {
it("should allow creating a valid ChopsticksConfig with kebab-case keys", () => {
const config = {
endpoint: "wss://rpc.polkadot.io",
block: 12345,
port: 8e3,
host: "127.0.0.1",
"build-block-mode": BuildBlockMode.Manual,
"wasm-override": "/path/to/wasm",
"allow-unresolved-imports": true,
"mock-signature-host": true,
db: "./chopsticks.db",
"runtime-log-level": 3,
"rpc-timeout": 3e4
// New field supported via chopsticks type
};
expect(config.endpoint).toBe("wss://rpc.polkadot.io");
expect(config.block).toBe(12345);
expect(config.port).toBe(8e3);
expect(config["rpc-timeout"]).toBe(3e4);
});
it("should allow ChopsticksConfig with required port and build-block-mode", () => {
const config = {
endpoint: "wss://rpc.polkadot.io",
port: 8e3,
"build-block-mode": BuildBlockMode.Manual
};
expect(config.endpoint).toBe("wss://rpc.polkadot.io");
expect(config.port).toBe(8e3);
expect(config["build-block-mode"]).toBe(BuildBlockMode.Manual);
});
it("should allow providing ChopsticksConfig via Layer", async () => {
const config = {
endpoint: "wss://test.io",
port: 9e3,
"build-block-mode": BuildBlockMode.Manual
};
const configLayer = Layer.succeed(ChopsticksConfigTag, config);
const program = Effect2.gen(function* () {
const cfg = yield* ChopsticksConfigTag;
return cfg.endpoint;
}).pipe(Effect2.provide(configLayer));
const result = await Effect2.runPromise(program);
expect(result).toBe("wss://test.io");
});
});
describe("Block Creation Types", () => {
it("should validate BlockCreationParams type", () => {
const params = {
count: 5,
to: 100,
transactions: ["0x1234", "0x5678"],
ump: { 2e3: ["0xmsg1", "0xmsg2"] },
dmp: [{ sentAt: 1, msg: "0xdmp" }],
hrmp: { 2001: [{ sentAt: 2, data: "0xhrmp" }] }
};
expect(params.count).toBe(5);
expect(params.transactions?.length).toBe(2);
});
it("should validate BlockCreationResult type", () => {
const result = {
block: {
hash: "0xabcdef",
number: 42
}
};
expect(result.block.number).toBe(42);
expect(result.block.hash).toBe("0xabcdef");
});
it("should validate DryRunResult type", () => {
const result = {
success: true,
storageDiff: [
["0xkey1", "0xvalue1"],
["0xkey2", null]
],
error: void 0
};
expect(result.success).toBe(true);
expect(result.storageDiff.length).toBe(2);
});
});
});