Initial commit: add .gitignore and README
This commit is contained in:
107
tests/unit/guards/oracleSanity.test.ts
Normal file
107
tests/unit/guards/oracleSanity.test.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { evaluateOracleSanity } from "../../../src/guards/oracleSanity.js";
|
||||
import { PriceOracle } from "../../../src/pricing/index.js";
|
||||
import { Guard } from "../../../src/strategy.schema.js";
|
||||
|
||||
describe("Oracle Sanity Guard", () => {
|
||||
let mockOracle: PriceOracle;
|
||||
let mockProvider: any;
|
||||
|
||||
beforeEach(() => {
|
||||
mockProvider = {
|
||||
getNetwork: vi.fn().mockResolvedValue({ chainId: 1n }),
|
||||
call: vi.fn(),
|
||||
};
|
||||
|
||||
mockOracle = new PriceOracle("mainnet");
|
||||
// @ts-ignore - access private property for testing
|
||||
mockOracle.provider = mockProvider;
|
||||
});
|
||||
|
||||
it("should pass when price is within bounds", async () => {
|
||||
const guard: Guard = {
|
||||
type: "oracleSanity",
|
||||
params: {
|
||||
token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
maxDeviationBps: 500, // 5%
|
||||
},
|
||||
};
|
||||
|
||||
// Mock price fetch
|
||||
vi.spyOn(mockOracle, "getPrice").mockResolvedValue({
|
||||
name: "chainlink",
|
||||
price: 1000000n, // $1.00
|
||||
decimals: 8,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
const result = await evaluateOracleSanity(guard, mockOracle, {});
|
||||
expect(result.passed).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail when price deviation is too high", async () => {
|
||||
const guard: Guard = {
|
||||
type: "oracleSanity",
|
||||
params: {
|
||||
token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
maxDeviationBps: 100, // 1%
|
||||
expectedPrice: "1000000", // $1.00
|
||||
},
|
||||
};
|
||||
|
||||
// Mock price that's 2% off
|
||||
vi.spyOn(mockOracle, "getPrice").mockResolvedValue({
|
||||
name: "chainlink",
|
||||
price: 1020000n, // $1.02 (2% deviation)
|
||||
decimals: 8,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
const result = await evaluateOracleSanity(guard, mockOracle, {});
|
||||
expect(result.passed).toBe(false);
|
||||
expect(result.reason).toContain("deviation");
|
||||
});
|
||||
|
||||
it("should handle missing oracle gracefully", async () => {
|
||||
const guard: Guard = {
|
||||
type: "oracleSanity",
|
||||
params: {
|
||||
token: "0xInvalid",
|
||||
maxDeviationBps: 500,
|
||||
},
|
||||
};
|
||||
|
||||
vi.spyOn(mockOracle, "getPrice").mockRejectedValue(
|
||||
new Error("Oracle not found")
|
||||
);
|
||||
|
||||
const result = await evaluateOracleSanity(guard, mockOracle, {});
|
||||
expect(result.passed).toBe(false);
|
||||
expect(result.reason).toBeDefined();
|
||||
});
|
||||
|
||||
it("should check for stale price data", async () => {
|
||||
const guard: Guard = {
|
||||
type: "oracleSanity",
|
||||
params: {
|
||||
token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
maxDeviationBps: 500,
|
||||
maxAgeSeconds: 3600, // 1 hour
|
||||
},
|
||||
};
|
||||
|
||||
// Mock stale price (2 hours old)
|
||||
const staleTimestamp = Date.now() - 2 * 60 * 60 * 1000;
|
||||
vi.spyOn(mockOracle, "getPrice").mockResolvedValue({
|
||||
name: "chainlink",
|
||||
price: 1000000n,
|
||||
decimals: 8,
|
||||
timestamp: staleTimestamp,
|
||||
});
|
||||
|
||||
const result = await evaluateOracleSanity(guard, mockOracle, {});
|
||||
expect(result.passed).toBe(false);
|
||||
expect(result.reason).toContain("stale");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user