202 lines
6.6 KiB
TypeScript
202 lines
6.6 KiB
TypeScript
|
|
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||
|
|
import {
|
||
|
|
isCompatibleVersion,
|
||
|
|
getVersionMismatchError,
|
||
|
|
KRAIKEN_LIB_VERSION,
|
||
|
|
COMPATIBLE_CONTRACT_VERSIONS,
|
||
|
|
} from 'kraiken-lib/version';
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// isCompatibleVersion (from kraiken-lib/version, aliased to source)
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
describe('isCompatibleVersion', () => {
|
||
|
|
it('returns true for each version in COMPATIBLE_CONTRACT_VERSIONS', () => {
|
||
|
|
for (const v of COMPATIBLE_CONTRACT_VERSIONS) {
|
||
|
|
expect(isCompatibleVersion(v)).toBe(true);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns false for version 0', () => {
|
||
|
|
expect(isCompatibleVersion(0)).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns false for a future version not yet listed', () => {
|
||
|
|
expect(isCompatibleVersion(9999)).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns false for negative version', () => {
|
||
|
|
expect(isCompatibleVersion(-1)).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// getVersionMismatchError (from kraiken-lib/version)
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
describe('getVersionMismatchError', () => {
|
||
|
|
it('includes the bad contract version in the output (ponder context)', () => {
|
||
|
|
const msg = getVersionMismatchError(99, 'ponder');
|
||
|
|
expect(msg).toContain('99');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('includes the library version in the output', () => {
|
||
|
|
const msg = getVersionMismatchError(99, 'ponder');
|
||
|
|
expect(msg).toContain(String(KRAIKEN_LIB_VERSION));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('includes ponder-specific remediation steps', () => {
|
||
|
|
const msg = getVersionMismatchError(99, 'ponder');
|
||
|
|
expect(msg).toContain('COMPATIBLE_CONTRACT_VERSIONS');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('includes frontend-specific remediation steps', () => {
|
||
|
|
const msg = getVersionMismatchError(99, 'frontend');
|
||
|
|
expect(msg).toContain('administrator');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('formats compatible versions list', () => {
|
||
|
|
const msg = getVersionMismatchError(99, 'ponder');
|
||
|
|
for (const v of COMPATIBLE_CONTRACT_VERSIONS) {
|
||
|
|
expect(msg).toContain(String(v));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns a multi-line string (box drawing)', () => {
|
||
|
|
const msg = getVersionMismatchError(99, 'ponder');
|
||
|
|
expect(msg.split('\n').length).toBeGreaterThan(3);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// validateContractVersion (helpers/version.ts) — mock the Ponder context
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
describe('validateContractVersion', () => {
|
||
|
|
afterEach(() => {
|
||
|
|
vi.restoreAllMocks();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('logs success and upserts metadata on compatible version', async () => {
|
||
|
|
const { validateContractVersion } = await import('../helpers/version.js');
|
||
|
|
|
||
|
|
const setFn = vi.fn().mockResolvedValue(undefined);
|
||
|
|
const ctx = {
|
||
|
|
client: {
|
||
|
|
readContract: vi.fn().mockResolvedValue(KRAIKEN_LIB_VERSION),
|
||
|
|
},
|
||
|
|
contracts: {
|
||
|
|
Kraiken: { abi: [], address: '0x0001' as `0x${string}` },
|
||
|
|
},
|
||
|
|
db: {
|
||
|
|
find: vi.fn().mockResolvedValue(null),
|
||
|
|
insert: vi.fn().mockReturnValue({ values: vi.fn().mockResolvedValue(undefined) }),
|
||
|
|
update: vi.fn().mockReturnValue({ set: setFn }),
|
||
|
|
},
|
||
|
|
logger: {
|
||
|
|
info: vi.fn(),
|
||
|
|
error: vi.fn(),
|
||
|
|
warn: vi.fn(),
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as () => never);
|
||
|
|
|
||
|
|
await validateContractVersion(ctx as unknown as import('ponder:registry').Context);
|
||
|
|
|
||
|
|
expect(exitSpy).not.toHaveBeenCalled();
|
||
|
|
expect(ctx.logger.info).toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('calls process.exit on incompatible contract version', async () => {
|
||
|
|
const { validateContractVersion } = await import('../helpers/version.js');
|
||
|
|
|
||
|
|
const ctx = {
|
||
|
|
client: {
|
||
|
|
readContract: vi.fn().mockResolvedValue(9999n),
|
||
|
|
},
|
||
|
|
contracts: {
|
||
|
|
Kraiken: { abi: [], address: '0x0001' as `0x${string}` },
|
||
|
|
},
|
||
|
|
db: {
|
||
|
|
find: vi.fn().mockResolvedValue(null),
|
||
|
|
insert: vi.fn().mockReturnValue({ values: vi.fn().mockResolvedValue(undefined) }),
|
||
|
|
update: vi.fn().mockReturnValue({ set: vi.fn().mockResolvedValue(undefined) }),
|
||
|
|
},
|
||
|
|
logger: {
|
||
|
|
info: vi.fn(),
|
||
|
|
error: vi.fn(),
|
||
|
|
warn: vi.fn(),
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as () => never);
|
||
|
|
|
||
|
|
await validateContractVersion(ctx as unknown as import('ponder:registry').Context);
|
||
|
|
|
||
|
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('calls process.exit when readContract throws', async () => {
|
||
|
|
const { validateContractVersion } = await import('../helpers/version.js');
|
||
|
|
|
||
|
|
const ctx = {
|
||
|
|
client: {
|
||
|
|
readContract: vi.fn().mockRejectedValue(new Error('network error')),
|
||
|
|
},
|
||
|
|
contracts: {
|
||
|
|
Kraiken: { abi: [], address: '0x0001' as `0x${string}` },
|
||
|
|
},
|
||
|
|
db: {
|
||
|
|
find: vi.fn().mockResolvedValue(null),
|
||
|
|
insert: vi.fn().mockReturnValue({ values: vi.fn().mockResolvedValue(undefined) }),
|
||
|
|
update: vi.fn().mockReturnValue({ set: vi.fn().mockResolvedValue(undefined) }),
|
||
|
|
},
|
||
|
|
logger: {
|
||
|
|
info: vi.fn(),
|
||
|
|
error: vi.fn(),
|
||
|
|
warn: vi.fn(),
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as () => never);
|
||
|
|
|
||
|
|
await validateContractVersion(ctx as unknown as import('ponder:registry').Context);
|
||
|
|
|
||
|
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('updates existing metadata when a row already exists', async () => {
|
||
|
|
const { validateContractVersion } = await import('../helpers/version.js');
|
||
|
|
|
||
|
|
const setFn = vi.fn().mockResolvedValue(undefined);
|
||
|
|
const existingMeta = { id: 'stack-meta', contractVersion: 1 };
|
||
|
|
const ctx = {
|
||
|
|
client: {
|
||
|
|
readContract: vi.fn().mockResolvedValue(KRAIKEN_LIB_VERSION),
|
||
|
|
},
|
||
|
|
contracts: {
|
||
|
|
Kraiken: { abi: [], address: '0x0001' as `0x${string}` },
|
||
|
|
},
|
||
|
|
db: {
|
||
|
|
find: vi.fn().mockResolvedValue(existingMeta),
|
||
|
|
insert: vi.fn().mockReturnValue({ values: vi.fn().mockResolvedValue(undefined) }),
|
||
|
|
update: vi.fn().mockReturnValue({ set: setFn }),
|
||
|
|
},
|
||
|
|
logger: {
|
||
|
|
info: vi.fn(),
|
||
|
|
error: vi.fn(),
|
||
|
|
warn: vi.fn(),
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as () => never);
|
||
|
|
|
||
|
|
await validateContractVersion(ctx as unknown as import('ponder:registry').Context);
|
||
|
|
|
||
|
|
expect(exitSpy).not.toHaveBeenCalled();
|
||
|
|
expect(ctx.db.update).toHaveBeenCalled();
|
||
|
|
expect(ctx.db.insert).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
});
|