Files
Deep-Package-Inspection/test/sync_test.js
T

198 lines
5.5 KiB
JavaScript

// test/sync_test.js
// TDD Integration Test: Verifikasi sinkronisasi 100% file antara Localhost dan Domain
'use strict';
const { Client } = require('ssh2');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
// Load environment variables if available
try {
require('dotenv').config({ path: path.join(__dirname, '../.env.local') });
} catch (e) {}
const SSH = {
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
readyTimeout: 60000
};
const REMOTE_ROOT = '/home/adminbackend/web/dev.demoplace.my.id/public_html';
const LOCAL_ROOT = path.join(__dirname, '..');
let testCount = 0;
let passedCount = 0;
function assert(label, condition) {
testCount++;
if (condition) {
console.log(` ✓ PASSED: ${label}`);
passedCount++;
} else {
console.error(` ✗ FAILED: ${label}`);
}
}
function getMD5(filePath) {
try {
const data = fs.readFileSync(filePath);
return crypto.createHash('md5').update(data).digest('hex');
} catch (e) {
return null;
}
}
function getFilesLocal(dir, files = []) {
const list = fs.readdirSync(dir);
for (const item of list) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
if (item !== 'node_modules' && item !== '.next' && item !== '.git') {
getFilesLocal(fullPath, files);
}
} else {
files.push(path.relative(LOCAL_ROOT, fullPath).replace(/\\/g, '/'));
}
}
return files;
}
const remoteScript = `
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const root = '/home/adminbackend/web/dev.demoplace.my.id/public_html';
function getMD5(filePath) {
try {
const data = fs.readFileSync(filePath);
return crypto.createHash('md5').update(data).digest('hex');
} catch (e) {
return null;
}
}
function getFilesRemote(dir, files = {}) {
try {
const list = fs.readdirSync(dir);
for (const item of list) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
if (item !== 'node_modules' && item !== '.next' && item !== '.git') {
getFilesRemote(fullPath, files);
}
} else {
const rel = path.relative(root, fullPath);
files[rel] = { md5: getMD5(fullPath), size: stat.size };
}
}
} catch(e) {}
return files;
}
const filesMap = {};
getFilesRemote(path.join(root, 'src'), filesMap);
getFilesRemote(path.join(root, 'public'), filesMap);
getFilesRemote(path.join(root, 'backend'), filesMap);
getFilesRemote(path.join(root, 'proxy'), filesMap);
const rootConfigs = ['package.json', 'tsconfig.json', 'next.config.ts', '.env.production'];
for (const f of rootConfigs) {
const full = path.join(root, f);
if (fs.existsSync(full)) {
const stat = fs.statSync(full);
filesMap[f] = { md5: getMD5(full), size: stat.size };
}
}
console.log(JSON.stringify(filesMap));
`;
async function runTests() {
console.log('\n=========================================');
console.log(' RUNNING TDD SYNC COMPARISON TESTS');
console.log('=========================================\n');
let remoteFiles = {};
try {
const conn = new Client();
const remoteFilesStr = await new Promise((resolve, reject) => {
conn.on('ready', () => {
const b64 = Buffer.from(remoteScript).toString('base64');
conn.exec(`echo "${b64}" | base64 -d | node`, (err, stream) => {
if (err) return reject(err);
let out = '';
let errOut = '';
stream.on('data', d => out += d);
stream.stderr.on('data', d => errOut += d);
stream.on('close', () => {
conn.end();
if (errOut.trim()) {
reject(new Error(errOut));
} else {
resolve(out);
}
});
});
}).on('error', reject).connect(SSH);
});
remoteFiles = JSON.parse(remoteFilesStr);
console.log('✓ Connected to remote server and retrieved remote file list.');
} catch (err) {
console.error('✗ Failed to connect to remote server:', err.message);
process.exit(1);
}
// Get local files
const localFiles = {};
const pathsToCheck = ['src', 'public', 'backend', 'proxy'];
for (const p of pathsToCheck) {
const full = path.join(LOCAL_ROOT, p);
if (fs.existsSync(full)) {
const files = getFilesLocal(full);
for (const f of files) {
const localPath = path.join(LOCAL_ROOT, f);
localFiles[f] = { md5: getMD5(localPath) };
}
}
}
const rootConfigs = ['package.json', 'tsconfig.json', 'next.config.ts', '.env.production'];
for (const f of rootConfigs) {
const full = path.join(LOCAL_ROOT, f);
if (fs.existsSync(full)) {
localFiles[f] = { md5: getMD5(full) };
}
}
// Perform assertions
for (const [relPath, info] of Object.entries(remoteFiles)) {
const localInfo = localFiles[relPath];
assert(`File "${relPath}" should exist locally`, !!localInfo);
if (localInfo) {
assert(`File "${relPath}" content should match remote exactly`, localInfo.md5 === info.md5);
}
}
console.log(`\n=========================================`);
console.log(` RESULT: ${passedCount} / ${testCount} tests passed.`);
console.log(`=========================================\n`);
if (passedCount !== testCount) {
process.exit(1);
} else {
process.exit(0);
}
}
runTests().catch(e => {
console.error('[FATAL]', e.message);
process.exit(1);
});