Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app/api/upgrade-config/__tests__/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('GET /api/upgrade-config', () => {
mockReaddir.mockResolvedValue(['config1.json']);
mockReadFile.mockResolvedValue('{}');
mockParseFromString.mockReturnValue({
result: { success: true },
success: true,
config: { ledgerId: 1 },
});
});
Expand Down Expand Up @@ -107,7 +107,7 @@ describe('GET /api/upgrade-config', () => {
});

it('returns the default config option when parsing fails', async () => {
mockParseFromString.mockReturnValue({ result: { success: false } });
mockParseFromString.mockReturnValue({ success: false });
const res = await GET(createRequest({ network: 'mainnet', upgradeId: 'test' }));
expect(res.status).toBe(200);
const body = await res.json();
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/upgrade-config/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export async function GET(req: NextRequest) {
const configContent = await fs.readFile(filePath, 'utf-8');
const parsedConfig = parseFromString(configContent);

if (parsedConfig.result.success && 'config' in parsedConfig) {
if (parsedConfig.success) {
ledgerId = parsedConfig.config.ledgerId;
} else {
console.warn(`Failed to parse ${configFile}, using default ledgerId: 0`);
Expand Down
47 changes: 47 additions & 0 deletions src/lib/__tests__/parser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { z } from 'zod';
import { getValidationSummary, parseFromString } from '../parser';

const config = {
cmd: 'forge script Test',
ledgerId: 0,
rpcUrl: 'http://localhost:8545',
expectedDomainAndMessageHashes: {
address: '0x0000000000000000000000000000000000000001',
domainHash: '0x' + 'a'.repeat(64),
messageHash: '0x' + 'b'.repeat(64),
},
stateOverrides: [],
stateChanges: [],
};

it('parses valid config JSON', () => {
expect(parseFromString(JSON.stringify(config))).toEqual({ success: true, config });
});

it('returns schema issues for invalid config JSON', () => {
const result = parseFromString(JSON.stringify({ ...config, ledgerId: -1 }));
expect(result.success).toBe(false);
if (result.success) throw new Error('Expected parsing to fail');
expect(result.zodError.issues).toEqual([
expect.objectContaining({ path: ['ledgerId'], code: 'too_small' }),
]);
});

it('returns one invalid JSON issue for malformed JSON', () => {
const result = parseFromString('{');
expect(result.success).toBe(false);
if (result.success) throw new Error('Expected parsing to fail');
expect(result.zodError.issues).toEqual([
{ code: 'custom', path: [], message: expect.stringMatching(/^Invalid JSON:/) },
]);
});

it('formats the exact failure summary', () => {
const failure = {
success: false as const,
zodError: new z.ZodError([{ code: 'custom', path: ['task', 'name'], message: 'Required' }]),
};
expect(getValidationSummary(failure)).toBe(
'❌ Configuration has errors\n\nErrors (1):\n • task.name: Required'
);
});
45 changes: 12 additions & 33 deletions src/lib/parser.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,24 @@
import { z } from 'zod';
import { TaskConfigSchema } from './config-schemas';
import { ParsedConfig, ParseResult } from './types/index';
import type { TaskConfig } from './types/index';

function parseConfig(jsonData: unknown): ParsedConfig {
const parsed = TaskConfigSchema.safeParse(jsonData);
type ParsingOutcome =
| { success: true; config: TaskConfig }
| { success: false; zodError: z.ZodError };

if (parsed.success) {
return {
config: parsed.data,
result: {
success: true,
},
};
}

return {
result: {
success: false,
zodError: parsed.error,
},
};
}

export function parseFromString(jsonString: string): ParsedConfig {
export function parseFromString(jsonString: string): ParsingOutcome {
try {
return parseConfig(JSON.parse(jsonString));
const parsed = TaskConfigSchema.safeParse(JSON.parse(jsonString));
if (parsed.success) {
return { success: true, config: parsed.data };
}
return { success: false, zodError: parsed.error };
} catch (error) {
return {
result: {
success: false,
zodError: createJsonParseError(error),
},
};
return { success: false, zodError: createJsonParseError(error) };
}
}

export function getValidationSummary(result: ParseResult): string {
if (result.success) {
return '✅ Configuration is valid';
}

export function getValidationSummary(result: Extract<ParsingOutcome, { success: false }>): string {
const parts: string[] = [
'❌ Configuration has errors',
`\nErrors (${result.zodError.issues.length}):`,
Expand Down
1 change: 0 additions & 1 deletion src/lib/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './validation-config';
export * from './parser';
export * from './diff';
export * from './validation-data';
export * from './comparison';
Expand Down
21 changes: 0 additions & 21 deletions src/lib/types/parser.ts

This file was deleted.

8 changes: 2 additions & 6 deletions src/lib/validation-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,11 @@ async function getConfigData(opts: ValidationServiceOpts): Promise<{

const parsedConfig = parseFromString(configContent);

if (!parsedConfig.result.success) {
console.error('❌ Failed to parse config:', getValidationSummary(parsedConfig.result));
if (!parsedConfig.success) {
console.error('❌ Failed to parse config:', getValidationSummary(parsedConfig));
throw new Error('ValidationService::getConfigData: Failed to parse config file');
}

if (!('config' in parsedConfig)) {
throw new Error('ValidationService::getConfigData: Parsed config missing config data');
}

console.log(`✅ Loaded config data from ${configFileName}`);
return {
cfg: parsedConfig.config,
Expand Down
Loading