test erweiterungen

This commit is contained in:
kuro 2026-10-12 22:23:29 +02:00
parent ba836ee30f
commit 1910b0def2
2 changed files with 89 additions and 57 deletions

View File

@ -1,38 +1,64 @@
import * as assert from 'assert'; import * as assert from 'assert';
import * as vscode from 'vscode';
import { BuiltInCompletion } from '../../providers/completions/BuiltInCompletion'; import { BuiltInCompletion } from '../../providers/completions/BuiltInCompletion';
import { SnippetCompletion } from '../../providers/completions/SnippetCompletion'; import { SnippetCompletion } from '../../providers/completions/SnippetCompletion';
import { LocalContextCompletion } from '../../providers/completions/LocalContextCompletion';
// Hilfsfunktion simuliert ein vscode.TextDocument
function createMockDoc(text: string): any {
const lines = text.split('\n');
return {
getText: () => text,
lineAt: (i: number) => ({ text: lines[i] })
};
}
suite('Completion Provider Test Suite', () => { suite('Completion Provider Test Suite', () => {
test('BuiltInCompletion: Reagiert auf "log."', () => { test('SnippetCompletion: Kontext-Check (Kein if außerhalb des Bodys)', () => {
const strategy = new BuiltInCompletion();
// Wir faken Document und Position, da wir nur linePrefix brauchen
const result = strategy.provideCompletions(null as any, null as any, ' log.');
assert.ok(result !== undefined);
assert.strictEqual(result.length, 4); // debug, info, warn, error
assert.strictEqual(result[0].label, 'debug');
});
test('BuiltInCompletion: Ignoriert normalen Text', () => {
const strategy = new BuiltInCompletion();
const result = strategy.provideCompletions(null as any, null as any, ' my_var');
assert.strictEqual(result, undefined);
});
test('SnippetCompletion: Liefert Snippets für normalen Text', () => {
const strategy = new SnippetCompletion(); const strategy = new SnippetCompletion();
const result = strategy.provideCompletions(null as any, null as any, ' i');
// Simuliert, dass wir im "metadata" Block tippen
const docText = `pattern Test 1.0\nmetadata\n `;
const mockDoc = createMockDoc(docText);
const position = new vscode.Position(2, 4); // Zeile 2, Cursor am Ende
const result = strategy.provideCompletions(mockDoc, position, ' ');
assert.ok(result !== undefined); assert.ok(result !== undefined);
const hasIf = result.some(item => item.label === 'if'); const hasIf = result.some(item => item.label === 'if');
assert.ok(hasIf, "Muss 'if' Snippet vorschlagen"); assert.strictEqual(hasIf, false, "Darf kein 'if' im metadata Block vorschlagen");
}); });
test('SnippetCompletion: Blockiert Snippets nach Punkt', () => { test('SnippetCompletion: Vorschläge im Body korrekt', () => {
const strategy = new SnippetCompletion(); const strategy = new SnippetCompletion();
const result = strategy.provideCompletions(null as any, null as any, ' discovery.r');
assert.strictEqual(result, undefined, "Darf keine Block-Snippets in Namespaces anzeigen"); // Simuliert, dass wir im "body" tippen
const docText = `pattern Test 1.0\nbody\n `;
const mockDoc = createMockDoc(docText);
const position = new vscode.Position(2, 4);
const result = strategy.provideCompletions(mockDoc, position, ' ');
assert.ok(result !== undefined);
const hasIf = result.some(item => item.label === 'if');
assert.strictEqual(hasIf, true, "Muss 'if' im Body vorschlagen");
});
test('LocalContextCompletion: Erkennt lokale defines in Namespaces', async () => {
const strategy = new LocalContextCompletion();
const docText = `definitions my_tools 1.0\n define doWork(host)\n end define;\nend definitions;`;
const mockDoc = createMockDoc(docText);
const position = new vscode.Position(4, 0);
const result = await strategy.provideCompletions(mockDoc, position, 'do');
assert.ok(result !== undefined);
const funcItem = result.find(item => item.label === 'my_tools.doWork');
assert.ok(funcItem !== undefined, "Muss die Methode inkl. Namespace extrahieren");
// Prüft, ob das generierte Snippet das Auto-Semikolon und den $0 Cursor hat
const snippet = funcItem?.insertText as vscode.SnippetString;
assert.ok(snippet.value.includes('my_tools.doWork(${1:params});$0'));
}); });
}); });

View File

@ -5,65 +5,71 @@ import { SyntaxRule } from '../../core/rules/SyntaxRule';
import { BlockRule } from '../../core/rules/BlockRule'; import { BlockRule } from '../../core/rules/BlockRule';
suite('Linter Test Suite', () => { suite('Linter Test Suite', () => {
test('SyntaxRule: Warnt bei "=" Zuweisung außerhalb von Bedingungen', () => { test('SyntaxRule: Ignoriert URLs und Sonderzeichen in Strings', () => {
const rule = new SyntaxRule(); const rule = new SyntaxRule();
const ctx = new LinterContext(); const ctx = new LinterContext();
ctx.activePhaseName = 'body'; // Kontext setzen, damit der strict-mode nicht meckert ctx.activePhaseName = 'body'; // Erlaubte Zone
const rawLine = " my_var = 123;"; const rawLine = ' log.debug("https://example.com/api?q=1//test");';
// HIER GEFIXT: Wir übergeben ein Array als 5. Argument
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]); rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
const diag = ctx.diagnostics.find(d => d.message.includes("':=' für Zuweisungen")); // Darf keinen Fehler werfen (weder fehlendes Semikolon noch kaputter Kommentar)
assert.ok(diag !== undefined, "Fehler für falsche Zuweisung erwartet");
});
test('SyntaxRule: Akzeptiert "=" als Vergleich in Bedingungen', () => {
const rule = new SyntaxRule();
const ctx = new LinterContext();
ctx.activePhaseName = 'body';
const rawLine = " if my_var = 123 then";
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
// Darf keinen Fehler werfen, da inConditionBlock automatisch auf true geht
assert.strictEqual(ctx.diagnostics.length, 0); assert.strictEqual(ctx.diagnostics.length, 0);
}); });
test('SyntaxRule: Findet fehlendes Semikolon', () => { test('SyntaxRule: Erfasst TODOs als Information', () => {
const rule = new SyntaxRule();
const ctx = new LinterContext();
const rawLine = ' // TODO: Diese Funktion refactoren';
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
assert.strictEqual(ctx.diagnostics.length, 1);
assert.strictEqual(ctx.diagnostics[0].severity, vscode.DiagnosticSeverity.Information);
assert.ok(ctx.diagnostics[0].message.includes("TODO: Diese Funktion refactoren"));
});
test('SyntaxRule: Multi-Line if-Bedingung ohne Semikolon-Fehler', () => {
const rule = new SyntaxRule(); const rule = new SyntaxRule();
const ctx = new LinterContext(); const ctx = new LinterContext();
ctx.activePhaseName = 'body'; ctx.activePhaseName = 'body';
const rawLine = " my_var := 123"; // Zeile 1: or-Umbruch
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]); rule.checkLine('if x = 1 or', 'if x = 1 or', 0, ctx, []);
assert.strictEqual(ctx.diagnostics.length, 0);
assert.strictEqual(ctx.inConditionBlock, true);
const diag = ctx.diagnostics.find(d => d.message.includes("Fehlendes Semikolon")); // Zeile 2: then-Abschluss
assert.ok(diag !== undefined, "Fehler für fehlendes Semikolon erwartet"); rule.checkLine('y = 2 then', 'y = 2 then', 1, ctx, []);
assert.strictEqual(ctx.diagnostics.length, 0, "'then' darf kein Semikolon am Ende verlangen!");
assert.strictEqual(ctx.inConditionBlock, false);
}); });
test('BlockRule: Erzwingt Module-Header', () => { test('SyntaxRule: Akzeptiert Zuweisungen in erlaubten Zonen (z.B. metadata)', () => {
const rule = new BlockRule(); const rule = new SyntaxRule();
const ctx = new LinterContext(); const ctx = new LinterContext();
ctx.inModuleMetadata = true; // Wir sind im Modul-Metadata
const rawLine = "pattern Test 1.0"; const rawLine = " origin := 'Custom';";
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]); rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
assert.strictEqual(ctx.diagnostics.length, 1); assert.strictEqual(ctx.diagnostics.length, 0, "Zuweisungen müssen in metadata erlaubt sein");
assert.ok(ctx.diagnostics[0].message.includes("tpl 1.x module"));
}); });
test('BlockRule: Findet fehlendes Pattern-Log im Body', () => { test('BlockRule: if/for/while Stack-Tracking', () => {
const rule = new BlockRule(); const rule = new BlockRule();
const ctx = new LinterContext(); const ctx = new LinterContext();
ctx.hasModuleDef = true; ctx.hasModuleDef = true;
ctx.inBlock = 'pattern'; ctx.inBlock = 'pattern';
ctx.currentPatternName = 'TestPattern';
const lines = ['body', ' x := 1;', 'end body;']; // Öffnet Block
rule.checkLine('body', 'body', 0, ctx, lines); rule.checkLine('if true then', 'if true then', 0, ctx, []);
assert.strictEqual(ctx.openControlBlocks.length, 1);
assert.strictEqual(ctx.openControlBlocks[0].type, 'if');
const logDiag = ctx.diagnostics.find(d => d.code === 'missing_log_start'); // Schließt falschen Block (end for statt end if)
assert.ok(logDiag !== undefined, "Erwartet Fehler für fehlendes Log-Statement"); rule.checkLine('end for;', 'end for;', 1, ctx, []);
assert.strictEqual(ctx.diagnostics.length, 1);
assert.ok(ctx.diagnostics[0].message.includes("Falsches Ende"));
}); });
}); });