UNPKG

@moonwall/cli

Version:

Testing framework for the Moon family of projects

266 lines (263 loc) 10.3 kB
// 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); }); }); });