Upload via GUI (7543 Dateien)
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const http = require('node:http');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
|
||||
require('../src/git/gitBinary.js').useBundledGit();
|
||||
const { registerFeatureIpc, parsePorcelainZ } = require('../src/features/featuresIpc.js');
|
||||
const { scanForSecrets, checkName } = require('../src/features/secretScan.js');
|
||||
|
||||
// Minimaler Gitea-Nachbau für Diagnose, PRs/Issues und Backup
|
||||
function fakeGitea() {
|
||||
const calls = [];
|
||||
const comments = [];
|
||||
const server = http.createServer((req, res) => {
|
||||
const u = new URL(req.url, 'http://x');
|
||||
calls.push(`${req.method} ${u.pathname}`);
|
||||
const send = (code, obj) => { res.statusCode = code; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(obj)); };
|
||||
let body = '';
|
||||
req.on('data', c => body += c);
|
||||
req.on('end', () => {
|
||||
const p = u.pathname;
|
||||
if (p === '/api/v1/version') return send(200, { version: '1.24.0' });
|
||||
if (p === '/api/v1/user') return send(200, { login: 'M_Viper' });
|
||||
if (p === '/api/v1/user/repos') {
|
||||
if (u.searchParams.get('page') && u.searchParams.get('page') !== '1') return send(200, []);
|
||||
return send(200, [{ name: 'deskhub', owner: { login: 'M_Viper' }, default_branch: 'main', empty: false,
|
||||
clone_url: `http://127.0.0.1:${server.address().port}/M_Viper/deskhub.git` }]);
|
||||
}
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/pulls') return send(200, [{ number: 3, title: 'Fix', state: 'open', user: { login: 'a' }, labels: [], head: { label: 'fix' }, base: { label: 'main' }, mergeable: true }]);
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/issues' && req.method === 'GET') return send(200, [{ number: 5, title: 'Bug', state: 'open', labels: [{ name: 'bug', color: 'ff0000' }] }]);
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/issues' && req.method === 'POST') return send(201, { number: 6, ...JSON.parse(body), state: 'open', labels: [] });
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/issues/5/comments' && req.method === 'POST') { comments.push(JSON.parse(body).body); return send(201, {}); }
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/issues/5/comments') return send(200, comments.map((b, i) => ({ id: i, body: b, user: { login: 'M_Viper' } })));
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/issues/5') return send(200, { number: 5, title: 'Bug', state: 'open', labels: [] });
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/pulls/3/merge') return send(200, {});
|
||||
if (p === '/api/v1/repos/M_Viper/deskhub/archive/main.zip') {
|
||||
res.statusCode = 200; res.setHeader('Content-Type', 'application/zip'); return res.end(Buffer.from('PK\u0003\u0004fake'));
|
||||
}
|
||||
send(404, { message: 'not found' });
|
||||
});
|
||||
});
|
||||
return new Promise(r => server.listen(0, '127.0.0.1', () => r({ server, calls, comments, url: `http://127.0.0.1:${server.address().port}` })));
|
||||
}
|
||||
|
||||
function setup(url) {
|
||||
const handlers = {};
|
||||
const userData = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-ud-'));
|
||||
registerFeatureIpc({
|
||||
ipcMain: { handle: (name, fn) => { handlers[name] = fn; } },
|
||||
app: { getPath: () => userData },
|
||||
readCredentials: () => ({ giteaURL: url, giteaToken: 'geheim123' }),
|
||||
buildItemsFromPaths: async (paths) => ({
|
||||
items: paths.map(p => ({ localFile: p, targetPath: path.basename(p) })), skipped: [], ignored: []
|
||||
}),
|
||||
clearGitTransportBlock: () => false
|
||||
});
|
||||
const call = (name, data) => handlers[name]({ sender: { send() {} } }, data);
|
||||
return { call, userData };
|
||||
}
|
||||
|
||||
test('Geheimnisse werden an Name und Inhalt erkannt, Vorlagen nicht', async () => {
|
||||
assert.ok(checkName('google-services.json'));
|
||||
assert.ok(checkName('.env'));
|
||||
assert.ok(checkName('release.keystore'));
|
||||
assert.strictEqual(checkName('.env.example'), null);
|
||||
assert.strictEqual(checkName('README.md'), null);
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-sec-'));
|
||||
const bot = path.join(dir, 'bot.js');
|
||||
fs.writeFileSync(bot, 'const TOKEN = "123456789:AA' + 'b'.repeat(33) + '";');
|
||||
const clean = path.join(dir, 'app.js');
|
||||
fs.writeFileSync(clean, 'console.log("hallo")');
|
||||
const found = await scanForSecrets([{ localFile: bot, size: fs.statSync(bot).size }, { localFile: clean, size: fs.statSync(clean).size }]);
|
||||
assert.match(found.get(bot), /Telegram/);
|
||||
assert.strictEqual(found.has(clean), false);
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('git status -z wird korrekt gelesen (inkl. Umbenennung)', () => {
|
||||
const files = parsePorcelainZ(' M src/a.js\0?? neu.txt\0R neu/name.js\0alt/name.js\0D weg.txt\0');
|
||||
assert.deepStrictEqual(files.map(f => [f.code, f.path, f.from]), [['M', 'src/a.js', null], ['?', 'neu.txt', null], ['R', 'neu/name.js', 'alt/name.js'], ['D', 'weg.txt', null]]);
|
||||
});
|
||||
|
||||
test('Lokale Änderungen: Status, Diff und Commit nur der ausgewählten Dateien', async () => {
|
||||
const { call } = setup('http://127.0.0.1:1');
|
||||
const repo = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-local-'));
|
||||
const git = (...a) => spawnSync('git', ['-C', repo, '-c', 'user.name=t', '-c', 'user.email=t@t', ...a], { encoding: 'utf8' });
|
||||
git('init', '-q');
|
||||
fs.writeFileSync(path.join(repo, 'a.txt'), 'eins\n');
|
||||
git('add', '.'); git('commit', '-qm', 'start');
|
||||
fs.writeFileSync(path.join(repo, 'a.txt'), 'zwei\n');
|
||||
fs.writeFileSync(path.join(repo, 'b.txt'), 'neu\n');
|
||||
|
||||
const st = await call('local-git', { action: 'status', folder: repo });
|
||||
assert.strictEqual(st.ok, true);
|
||||
assert.deepStrictEqual(st.files.map(f => f.path).sort(), ['a.txt', 'b.txt']);
|
||||
|
||||
const diff = await call('local-git', { action: 'diff', folder: repo, file: 'a.txt', code: 'M' });
|
||||
assert.match(diff.diff, /-eins/);
|
||||
assert.match(diff.diff, /\+zwei/);
|
||||
|
||||
const c = await call('local-git', { action: 'commit', folder: repo, files: ['a.txt'], message: 'nur a' });
|
||||
assert.strictEqual(c.ok, true, c.error);
|
||||
const after = await call('local-git', { action: 'status', folder: repo });
|
||||
assert.deepStrictEqual(after.files.map(f => f.path), ['b.txt']);
|
||||
|
||||
const notRepo = await call('local-git', { action: 'status', folder: os.tmpdir() + path.sep + 'gm-gibtsnicht-' + Date.now() });
|
||||
assert.strictEqual(notRepo.ok, false);
|
||||
fs.rmSync(repo, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('Upload-Vorschau markiert Geheimnisse', async () => {
|
||||
const { call } = setup('http://127.0.0.1:1');
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-prev-'));
|
||||
const a = path.join(dir, 'main.kt'); fs.writeFileSync(a, 'fun main() {}');
|
||||
const g = path.join(dir, 'google-services.json'); fs.writeFileSync(g, '{}');
|
||||
const r = await call('preview-upload-paths', { paths: [a, g], destPath: '' });
|
||||
assert.strictEqual(r.ok, true);
|
||||
assert.strictEqual(r.secretCount, 1);
|
||||
assert.ok(r.files.find(f => f.localFile === g).secret);
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('Diagnose, PRs/Issues und Backup gegen einen nachgebauten Server', async () => {
|
||||
const g = await fakeGitea();
|
||||
const { call } = setup(g.url);
|
||||
try {
|
||||
const d = await call('run-server-diagnosis', {});
|
||||
const byLabel = Object.fromEntries(d.checks.map(c => [c.label, c.status]));
|
||||
assert.strictEqual(byLabel['Gitea-API'], 'ok');
|
||||
assert.strictEqual(byLabel['Token'], 'ok');
|
||||
assert.strictEqual(byLabel['Erreichbar über IPv4'], 'ok');
|
||||
assert.ok(!JSON.stringify(d).includes('geheim123'), 'Token darf nicht in der Diagnose stehen');
|
||||
|
||||
const pulls = await call('gitea-collab', { action: 'listPulls', owner: 'M_Viper', repo: 'deskhub' });
|
||||
assert.strictEqual(pulls.items[0].head, 'fix');
|
||||
const issues = await call('gitea-collab', { action: 'listIssues', owner: 'M_Viper', repo: 'deskhub' });
|
||||
assert.strictEqual(issues.items[0].labels[0].name, 'bug');
|
||||
assert.strictEqual((await call('gitea-collab', { action: 'comment', owner: 'M_Viper', repo: 'deskhub', index: 5, body: 'Danke' })).ok, true);
|
||||
const detail = await call('gitea-collab', { action: 'get', owner: 'M_Viper', repo: 'deskhub', index: 5 });
|
||||
assert.strictEqual(detail.comments[0].body, 'Danke');
|
||||
assert.strictEqual((await call('gitea-collab', { action: 'merge', owner: 'M_Viper', repo: 'deskhub', index: 3, style: 'squash' })).ok, true);
|
||||
assert.strictEqual((await call('gitea-collab', { action: 'createIssue', owner: 'M_Viper', repo: 'deskhub', title: 'Neu' })).item.number, 6);
|
||||
|
||||
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-bk-'));
|
||||
await call('repo-backup-set-config', { targetDir: target, interval: 'daily', keep: 2 });
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const r = await call('repo-backup-run-now', {});
|
||||
assert.strictEqual(r.summary.ok, 1, JSON.stringify(r));
|
||||
await new Promise(res => setTimeout(res, 61000 / 60)); // eindeutige Zeitstempel
|
||||
}
|
||||
const zips = fs.readdirSync(path.join(target, 'M_Viper', 'deskhub-archiv'));
|
||||
assert.ok(zips.length <= 3 && zips.length >= 1);
|
||||
const state = await call('repo-backup-get-config', {});
|
||||
assert.strictEqual(state.config.lastResult.total, 1);
|
||||
fs.rmSync(target, { recursive: true, force: true });
|
||||
} finally {
|
||||
g.server.close();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user