mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 06:22:04 +00:00
test: update unit test for nodeName field in validation response
Update expected validation response to include nodeName field in warnings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
163
scripts/test-docker-fingerprint.ts
Normal file
163
scripts/test-docker-fingerprint.ts
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* Test Docker Host Fingerprinting
|
||||||
|
* Verifies that host machine characteristics are stable across container recreations
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
import { platform, arch } from 'os';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
|
console.log('=== Docker Host Fingerprinting Test ===\n');
|
||||||
|
|
||||||
|
function generateHostFingerprint(): string {
|
||||||
|
try {
|
||||||
|
const signals: string[] = [];
|
||||||
|
|
||||||
|
console.log('Collecting host signals...\n');
|
||||||
|
|
||||||
|
// CPU info (stable across container recreations)
|
||||||
|
if (existsSync('/proc/cpuinfo')) {
|
||||||
|
const cpuinfo = readFileSync('/proc/cpuinfo', 'utf-8');
|
||||||
|
const modelMatch = cpuinfo.match(/model name\s*:\s*(.+)/);
|
||||||
|
const coresMatch = cpuinfo.match(/processor\s*:/g);
|
||||||
|
|
||||||
|
if (modelMatch) {
|
||||||
|
const cpuModel = modelMatch[1].trim();
|
||||||
|
signals.push(cpuModel);
|
||||||
|
console.log('✓ CPU Model:', cpuModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coresMatch) {
|
||||||
|
const cores = `cores:${coresMatch.length}`;
|
||||||
|
signals.push(cores);
|
||||||
|
console.log('✓ CPU Cores:', coresMatch.length);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('✗ /proc/cpuinfo not available (Windows/Mac Docker)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory (stable)
|
||||||
|
if (existsSync('/proc/meminfo')) {
|
||||||
|
const meminfo = readFileSync('/proc/meminfo', 'utf-8');
|
||||||
|
const totalMatch = meminfo.match(/MemTotal:\s+(\d+)/);
|
||||||
|
|
||||||
|
if (totalMatch) {
|
||||||
|
const memory = `mem:${totalMatch[1]}`;
|
||||||
|
signals.push(memory);
|
||||||
|
console.log('✓ Total Memory:', totalMatch[1], 'kB');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('✗ /proc/meminfo not available (Windows/Mac Docker)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Docker network subnet
|
||||||
|
const networkInfo = getDockerNetworkInfo();
|
||||||
|
if (networkInfo) {
|
||||||
|
signals.push(networkInfo);
|
||||||
|
console.log('✓ Network Info:', networkInfo);
|
||||||
|
} else {
|
||||||
|
console.log('✗ Network info not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Platform basics (stable)
|
||||||
|
signals.push(platform(), arch());
|
||||||
|
console.log('✓ Platform:', platform());
|
||||||
|
console.log('✓ Architecture:', arch());
|
||||||
|
|
||||||
|
// Generate stable ID from all signals
|
||||||
|
console.log('\nCombined signals:', signals.join(' | '));
|
||||||
|
const fingerprint = signals.join('-');
|
||||||
|
const userId = createHash('sha256').update(fingerprint).digest('hex').substring(0, 16);
|
||||||
|
|
||||||
|
return userId;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating fingerprint:', error);
|
||||||
|
// Fallback
|
||||||
|
return createHash('sha256')
|
||||||
|
.update(`${platform()}-${arch()}-docker`)
|
||||||
|
.digest('hex')
|
||||||
|
.substring(0, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDockerNetworkInfo(): string | null {
|
||||||
|
try {
|
||||||
|
// Read routing table to get bridge network
|
||||||
|
if (existsSync('/proc/net/route')) {
|
||||||
|
const routes = readFileSync('/proc/net/route', 'utf-8');
|
||||||
|
const lines = routes.split('\n');
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes('eth0')) {
|
||||||
|
const parts = line.split(/\s+/);
|
||||||
|
if (parts[2]) {
|
||||||
|
const gateway = parseInt(parts[2], 16).toString(16);
|
||||||
|
return `net:${gateway}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore errors
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test environment detection
|
||||||
|
console.log('\n=== Environment Detection ===\n');
|
||||||
|
|
||||||
|
const isDocker = process.env.IS_DOCKER === 'true';
|
||||||
|
const isCloudEnvironment = !!(
|
||||||
|
process.env.RAILWAY_ENVIRONMENT ||
|
||||||
|
process.env.RENDER ||
|
||||||
|
process.env.FLY_APP_NAME ||
|
||||||
|
process.env.HEROKU_APP_NAME ||
|
||||||
|
process.env.AWS_EXECUTION_ENV ||
|
||||||
|
process.env.KUBERNETES_SERVICE_HOST
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('IS_DOCKER env:', process.env.IS_DOCKER);
|
||||||
|
console.log('Docker detected:', isDocker);
|
||||||
|
console.log('Cloud environment:', isCloudEnvironment);
|
||||||
|
|
||||||
|
// Generate fingerprints
|
||||||
|
console.log('\n=== Fingerprint Generation ===\n');
|
||||||
|
|
||||||
|
const fingerprint1 = generateHostFingerprint();
|
||||||
|
const fingerprint2 = generateHostFingerprint();
|
||||||
|
const fingerprint3 = generateHostFingerprint();
|
||||||
|
|
||||||
|
console.log('\nFingerprint 1:', fingerprint1);
|
||||||
|
console.log('Fingerprint 2:', fingerprint2);
|
||||||
|
console.log('Fingerprint 3:', fingerprint3);
|
||||||
|
|
||||||
|
const consistent = fingerprint1 === fingerprint2 && fingerprint2 === fingerprint3;
|
||||||
|
console.log('\nConsistent:', consistent ? '✓ YES' : '✗ NO');
|
||||||
|
|
||||||
|
// Test explicit ID override
|
||||||
|
console.log('\n=== Environment Variable Override Test ===\n');
|
||||||
|
|
||||||
|
if (process.env.N8N_MCP_USER_ID) {
|
||||||
|
console.log('Explicit user ID:', process.env.N8N_MCP_USER_ID);
|
||||||
|
console.log('This would override the fingerprint');
|
||||||
|
} else {
|
||||||
|
console.log('No explicit user ID set');
|
||||||
|
console.log('To test: N8N_MCP_USER_ID=my-custom-id npx tsx ' + process.argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stability estimate
|
||||||
|
console.log('\n=== Stability Analysis ===\n');
|
||||||
|
|
||||||
|
const hasStableSignals = existsSync('/proc/cpuinfo') || existsSync('/proc/meminfo');
|
||||||
|
if (hasStableSignals) {
|
||||||
|
console.log('✓ Host-based signals available');
|
||||||
|
console.log('✓ Fingerprint should be stable across container recreations');
|
||||||
|
console.log('✓ Different fingerprints on different physical hosts');
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ Limited host signals (Windows/Mac Docker Desktop)');
|
||||||
|
console.log('⚠️ Fingerprint may not be fully stable');
|
||||||
|
console.log('💡 Recommendation: Use N8N_MCP_USER_ID env var for stability');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n');
|
||||||
@@ -980,6 +980,7 @@ describe('handlers-n8n-manager', () => {
|
|||||||
warnings: [
|
warnings: [
|
||||||
{
|
{
|
||||||
node: 'node1',
|
node: 'node1',
|
||||||
|
nodeName: 'node1',
|
||||||
message: 'Consider using newer version',
|
message: 'Consider using newer version',
|
||||||
details: { currentVersion: 1, latestVersion: 2 },
|
details: { currentVersion: 1, latestVersion: 2 },
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user