harb/services/ponder/src/tests/version.test.ts

202 lines
6.6 KiB
TypeScript
Raw Normal View History

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();
});
});