Fix tests
This commit is contained in:
@@ -56,7 +56,7 @@ export class RatesService extends EventEmitter<RatesServiceEventMap> {
|
||||
private unsubscribeFromAdapter: (() => void) | null = null;
|
||||
private started = false;
|
||||
|
||||
private constructor(adapter: RatesAdapter) {
|
||||
constructor(adapter: RatesAdapter) {
|
||||
super();
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
expectLogs,
|
||||
type LogExpectation,
|
||||
} from "../mocks/command";
|
||||
import { State } from "@xo-cash/state";
|
||||
|
||||
// ============================================================================
|
||||
// Error Cases - Validate argument parsing and error handling
|
||||
@@ -156,7 +157,8 @@ describe("invitation command - error cases", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-errors-"));
|
||||
@@ -196,7 +198,8 @@ describe("invitation command - receive flow", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-receive-"));
|
||||
@@ -308,7 +311,8 @@ describe("invitation command - request satoshis flow", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-request-"));
|
||||
@@ -396,12 +400,15 @@ describe("invitation command - request satoshis flow", () => {
|
||||
|
||||
describe("invitation command - send flow with resources", () => {
|
||||
let engine: Engine;
|
||||
let state: State;
|
||||
let app: AppService;
|
||||
let tempDir: string;
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
state = mockEngine.state;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-send-"));
|
||||
@@ -497,7 +504,7 @@ describe("invitation command - send flow with resources", () => {
|
||||
* This validates our test infrastructure works correctly.
|
||||
*/
|
||||
test("fake resources are accessible via engine", async () => {
|
||||
const resource = await addFakeResource(engine, {
|
||||
const resource = await addFakeResource(state!, {
|
||||
valueSatoshis: 50000,
|
||||
templateIdentifier: p2pkhTemplateIdentifier,
|
||||
outputIdentifier: "receiveOutput",
|
||||
@@ -526,7 +533,8 @@ describe("invitation command - multi-step append", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-append-"));
|
||||
@@ -673,7 +681,8 @@ describe("invitation command - list and inspect", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-list-"));
|
||||
@@ -841,7 +850,8 @@ describe("invitation command - sign flow", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-sign-"));
|
||||
@@ -961,7 +971,8 @@ describe("invitation command - import flow", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-import-"));
|
||||
@@ -1092,7 +1103,8 @@ describe("invitation command - auto-inputs flow", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-autoinputs-"));
|
||||
@@ -1167,7 +1179,8 @@ describe("invitation command - broadcast flow", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-broadcast-"));
|
||||
@@ -1231,7 +1244,8 @@ describe("invitation command - full lifecycle", () => {
|
||||
let paths: CommandPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-invitation-lifecycle-"));
|
||||
|
||||
@@ -81,7 +81,8 @@ describe("receive command", () => {
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
|
||||
app = await createMockAppService(engine);
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
expectLogs,
|
||||
type LogExpectation,
|
||||
} from "../mocks/command";
|
||||
import { State } from "@xo-cash/state";
|
||||
|
||||
type TestCase = {
|
||||
name: string;
|
||||
@@ -120,7 +121,8 @@ describe("resource command", () => {
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
|
||||
app = await createMockAppService(engine);
|
||||
@@ -181,11 +183,14 @@ describe("resource command", () => {
|
||||
|
||||
describe("resource command with populated data", () => {
|
||||
let engine: Engine;
|
||||
let state: State;
|
||||
let app: AppService;
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
state = mockEngine.state;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
app = await createMockAppService(engine);
|
||||
tempDir = mkdtempSync(path.join(tmpdir(), "xo-cli-resource-tests-"));
|
||||
@@ -197,8 +202,8 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("list returns count when resources exist", async () => {
|
||||
await addFakeResource(engine, { valueSatoshis: 50000 });
|
||||
await addFakeResource(engine, { valueSatoshis: 25000 });
|
||||
await addFakeResource(state, { valueSatoshis: 50000 });
|
||||
await addFakeResource(state, { valueSatoshis: 25000 });
|
||||
|
||||
const { io, spies } = createMockIO();
|
||||
const result = await handleResourceCommand(
|
||||
@@ -212,8 +217,8 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("list shows total satoshis", async () => {
|
||||
await addFakeResource(engine, { valueSatoshis: 50000 });
|
||||
await addFakeResource(engine, { valueSatoshis: 25000 });
|
||||
await addFakeResource(state, { valueSatoshis: 50000 });
|
||||
await addFakeResource(state, { valueSatoshis: 25000 });
|
||||
|
||||
const { io, spies } = createMockIO();
|
||||
await handleResourceCommand(createCommandDeps(app, io), ["list"], {});
|
||||
@@ -222,8 +227,8 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("list excludes reserved resources by default", async () => {
|
||||
await addFakeResource(engine, { valueSatoshis: 50000 });
|
||||
await addFakeResource(engine, {
|
||||
await addFakeResource(state, { valueSatoshis: 50000 });
|
||||
await addFakeResource(state, {
|
||||
valueSatoshis: 25000,
|
||||
reservedBy: "inv-123",
|
||||
});
|
||||
@@ -239,12 +244,12 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("list reserved shows only reserved resources", async () => {
|
||||
await addFakeResource(engine, { valueSatoshis: 50000 });
|
||||
await addFakeResource(engine, {
|
||||
await addFakeResource(state, { valueSatoshis: 50000 });
|
||||
await addFakeResource(state, {
|
||||
valueSatoshis: 25000,
|
||||
reservedBy: "inv-123",
|
||||
});
|
||||
await addFakeResource(engine, {
|
||||
await addFakeResource(state, {
|
||||
valueSatoshis: 10000,
|
||||
reservedBy: "inv-456",
|
||||
});
|
||||
@@ -261,8 +266,8 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("list all shows both reserved and unreserved", async () => {
|
||||
await addFakeResource(engine, { valueSatoshis: 50000 });
|
||||
await addFakeResource(engine, {
|
||||
await addFakeResource(state, { valueSatoshis: 50000 });
|
||||
await addFakeResource(state, {
|
||||
valueSatoshis: 25000,
|
||||
reservedBy: "inv-123",
|
||||
});
|
||||
@@ -278,7 +283,7 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("unreserve releases a reserved UTXO", async () => {
|
||||
const resource = await addFakeResource(engine, {
|
||||
const resource = await addFakeResource(state, {
|
||||
valueSatoshis: 25000,
|
||||
reservedBy: "inv-123",
|
||||
});
|
||||
@@ -306,7 +311,7 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("unreserve reports when UTXO is not reserved", async () => {
|
||||
const resource = await addFakeResource(engine, { valueSatoshis: 25000 });
|
||||
const resource = await addFakeResource(state, { valueSatoshis: 25000 });
|
||||
|
||||
const { io, spies } = createMockIO();
|
||||
await handleResourceCommand(
|
||||
@@ -322,12 +327,12 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("unreserve-all releases all reserved UTXOs", async () => {
|
||||
await addFakeResource(engine, { valueSatoshis: 50000 });
|
||||
await addFakeResource(engine, {
|
||||
await addFakeResource(state, { valueSatoshis: 50000 });
|
||||
await addFakeResource(state, {
|
||||
valueSatoshis: 25000,
|
||||
reservedBy: "inv-123",
|
||||
});
|
||||
await addFakeResource(engine, {
|
||||
await addFakeResource(state, {
|
||||
valueSatoshis: 10000,
|
||||
reservedBy: "inv-456",
|
||||
});
|
||||
@@ -348,7 +353,7 @@ describe("resource command with populated data", () => {
|
||||
});
|
||||
|
||||
test("list displays outpoint information", async () => {
|
||||
const resource = await addFakeResource(engine, { valueSatoshis: 12345 });
|
||||
const resource = await addFakeResource(state, { valueSatoshis: 12345 });
|
||||
|
||||
const { io, spies } = createMockIO();
|
||||
await handleResourceCommand(createCommandDeps(app, io), ["list"], {});
|
||||
|
||||
@@ -183,7 +183,8 @@ describe("template command", () => {
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
engine = await createMockEngine(DEFAULT_SEED);
|
||||
const mockEngine = await createMockEngine(DEFAULT_SEED);
|
||||
engine = mockEngine.engine;
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
|
||||
app = await createMockAppService(engine);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
createStorageAdapter,
|
||||
State,
|
||||
StorageType,
|
||||
UnspentOutputStatus,
|
||||
type UnspentOutputData,
|
||||
} from "@xo-cash/state";
|
||||
import { InMemoryBlockchainProvider } from "@xo-cash/engine";
|
||||
@@ -13,6 +14,8 @@ import { binToHex, sha256 } from "@bitauth/libauth";
|
||||
import { AppService } from "../../../src/services/app";
|
||||
import { InMemoryStorage } from "../../../src/services/storage";
|
||||
import { MockElectrumService } from "./electrum-service";
|
||||
import { MockRatesService } from "./rates-service";
|
||||
import { RatesService } from "../../../src/services/rates";
|
||||
|
||||
export const DEFAULT_SEED =
|
||||
"page pencil stock planet limb cluster assault speak off joke private pioneer";
|
||||
@@ -57,11 +60,11 @@ export const randomTxHash = (): string => {
|
||||
* @returns The created UnspentOutputData object.
|
||||
*/
|
||||
export const addFakeResource = async (
|
||||
engine: Engine,
|
||||
state: State,
|
||||
options: FakeResourceOptions = {},
|
||||
): Promise<UnspentOutputData> => {
|
||||
const resource: UnspentOutputData = {
|
||||
status: "confirmed",
|
||||
status: UnspentOutputStatus.CONFIRMED,
|
||||
selectable: true,
|
||||
privacy: false,
|
||||
templateIdentifier: options.templateIdentifier ?? "test-template",
|
||||
@@ -76,7 +79,7 @@ export const addFakeResource = async (
|
||||
reservedBy: options.reservedBy,
|
||||
};
|
||||
|
||||
await engine.state.storeUnspentOutputData(resource);
|
||||
await state.storeUnspentOutputData(resource);
|
||||
return resource;
|
||||
};
|
||||
|
||||
@@ -88,12 +91,12 @@ export const addFakeResource = async (
|
||||
* @param invitationIdentifier - The invitation identifier to reserve for.
|
||||
*/
|
||||
export const reserveResource = async (
|
||||
engine: Engine,
|
||||
state: State,
|
||||
outpointTransactionHash: string,
|
||||
outpointIndex: number,
|
||||
invitationIdentifier: string,
|
||||
): Promise<void> => {
|
||||
await engine.state.executeBulkUnspentOutputReservation(
|
||||
await state.executeBulkUnspentOutputReservation(
|
||||
[{ outpointTransactionHash, outpointIndex }],
|
||||
true,
|
||||
invitationIdentifier,
|
||||
@@ -108,12 +111,12 @@ export const reserveResource = async (
|
||||
* @param invitationIdentifier - The invitation identifier to unreserve from.
|
||||
*/
|
||||
export const unreserveResource = async (
|
||||
engine: Engine,
|
||||
state: State,
|
||||
outpointTransactionHash: string,
|
||||
outpointIndex: number,
|
||||
invitationIdentifier: string,
|
||||
): Promise<void> => {
|
||||
await engine.state.executeBulkUnspentOutputReservation(
|
||||
await state.executeBulkUnspentOutputReservation(
|
||||
[{ outpointTransactionHash, outpointIndex }],
|
||||
false,
|
||||
invitationIdentifier,
|
||||
@@ -153,13 +156,14 @@ export const createMockEngine = async (seed: string) => {
|
||||
const engine = new Engine(seed, state, blockchainMonitor, blockchainProvider);
|
||||
await engine.initializeStateSync();
|
||||
|
||||
return engine;
|
||||
return { engine, state, blockchainMonitor, blockchainProvider };
|
||||
};
|
||||
|
||||
export const createMockAppService = async (engine: Engine) => {
|
||||
const storage = await InMemoryStorage.create();
|
||||
|
||||
const electrum = new MockElectrumService();
|
||||
const mockRates = new MockRatesService();
|
||||
const rates = new RatesService(mockRates);
|
||||
|
||||
const config = {
|
||||
syncServerUrl: "http://localhost:3000",
|
||||
@@ -170,5 +174,5 @@ export const createMockAppService = async (engine: Engine) => {
|
||||
invitationStoragePath: "test-invitations.db",
|
||||
};
|
||||
|
||||
return new AppService(engine, storage, config, electrum);
|
||||
return new AppService(engine, storage, config, rates);
|
||||
};
|
||||
|
||||
23
tests/cli/mocks/rates-service.ts
Normal file
23
tests/cli/mocks/rates-service.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { BaseRates } from "../../../src/utils/rates/base-rates";
|
||||
|
||||
export class MockRatesService extends BaseRates {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async getRate(numeratorUnitCode: string, denominatorUnitCode: string): Promise<number> {
|
||||
return 1;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
async listPairs(): Promise<Set<string>> {
|
||||
return new Set();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user