fix: enable schema-based resourceLocator mode validation

Root cause analysis revealed validator was looking at wrong path for
modes data. n8n stores modes at top level of properties, not nested
in typeOptions.

Changes:
- config-validator.ts: Changed from prop.typeOptions?.resourceLocator?.modes
  to prop.modes (lines 273-310)
- property-extractor.ts: Added modes field to normalizeProperties to
  capture mode definitions from n8n nodes
- Updated all test cases to match real n8n schema structure with modes
  at property top level
- Rebuilt database with modes field

Results:
- 100% coverage: All 70 resourceLocator nodes now have modes defined
- Schema-based validation now ACTIVE (was being skipped before)
- False positive eliminated: Google Sheets "name" mode now validates
- Helpful error messages showing actual allowed modes from schema

Testing:
- All 33 unit tests pass
- Verified with n8n-mcp-tester: valid "name" mode passes, invalid modes
  fail with clear error listing allowed options [list, url, id, name]

Fixes #304 (Google Sheets false positive)
Related to #306 (validator improvements)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-10-11 19:29:21 +02:00
parent 4625ebf64d
commit fc8fb66900
4 changed files with 24 additions and 36 deletions

View File

@@ -691,15 +691,12 @@ describe('ConfigValidator - Basic Validation', () => {
name: 'model',
type: 'resourceLocator',
required: true,
typeOptions: {
resourceLocator: {
modes: {
list: { displayName: 'List' },
id: { displayName: 'ID' },
url: { displayName: 'URL' }
}
}
}
// In real n8n, modes are at top level, not in typeOptions
modes: [
{ name: 'list', displayName: 'List' },
{ name: 'id', displayName: 'ID' },
{ name: 'url', displayName: 'URL' }
]
}
];
@@ -726,15 +723,12 @@ describe('ConfigValidator - Basic Validation', () => {
name: 'model',
type: 'resourceLocator',
required: true,
typeOptions: {
resourceLocator: {
modes: [
{ name: 'list', displayName: 'List' },
{ name: 'id', displayName: 'ID' },
{ name: 'custom', displayName: 'Custom' }
]
}
}
// Array format at top level (real n8n structure)
modes: [
{ name: 'list', displayName: 'List' },
{ name: 'id', displayName: 'ID' },
{ name: 'custom', displayName: 'Custom' }
]
}
];
@@ -757,11 +751,7 @@ describe('ConfigValidator - Basic Validation', () => {
name: 'model',
type: 'resourceLocator',
required: true,
typeOptions: {
resourceLocator: {
modes: 'invalid-string' // Malformed schema
}
}
modes: 'invalid-string' // Malformed schema at top level
}
];
@@ -785,11 +775,7 @@ describe('ConfigValidator - Basic Validation', () => {
name: 'model',
type: 'resourceLocator',
required: true,
typeOptions: {
resourceLocator: {
modes: {} // Empty object
}
}
modes: {} // Empty object at top level
}
];
@@ -800,7 +786,7 @@ describe('ConfigValidator - Basic Validation', () => {
expect(result.errors.some(e => e.property === 'model.mode')).toBe(false);
});
it('should skip mode validation when typeOptions not provided', () => {
it('should skip mode validation when modes not provided', () => {
const nodeType = '@n8n/n8n-nodes-langchain.lmChatOpenAi';
const config = {
model: {
@@ -813,7 +799,7 @@ describe('ConfigValidator - Basic Validation', () => {
name: 'model',
type: 'resourceLocator',
required: true
// No typeOptions - schema doesn't define modes
// No modes property - schema doesn't define modes
}
];