Thanks @glifocat! Clean skill package — good docs, solid tests, nice intent files. Pushed a small fix for path traversal on the PDF filename before merging.
172 lines
6.3 KiB
TypeScript
172 lines
6.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
describe('pdf-reader skill package', () => {
|
|
const skillDir = path.resolve(__dirname, '..');
|
|
|
|
it('has a valid manifest', () => {
|
|
const manifestPath = path.join(skillDir, 'manifest.yaml');
|
|
expect(fs.existsSync(manifestPath)).toBe(true);
|
|
|
|
const content = fs.readFileSync(manifestPath, 'utf-8');
|
|
expect(content).toContain('skill: add-pdf-reader');
|
|
expect(content).toContain('version: 1.1.0');
|
|
expect(content).toContain('container/Dockerfile');
|
|
});
|
|
|
|
it('has all files declared in adds', () => {
|
|
const skillMd = path.join(skillDir, 'add', 'container', 'skills', 'pdf-reader', 'SKILL.md');
|
|
const pdfReaderScript = path.join(skillDir, 'add', 'container', 'skills', 'pdf-reader', 'pdf-reader');
|
|
|
|
expect(fs.existsSync(skillMd)).toBe(true);
|
|
expect(fs.existsSync(pdfReaderScript)).toBe(true);
|
|
});
|
|
|
|
it('pdf-reader script is a valid Bash script', () => {
|
|
const scriptPath = path.join(skillDir, 'add', 'container', 'skills', 'pdf-reader', 'pdf-reader');
|
|
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
|
|
// Valid shell script
|
|
expect(content).toMatch(/^#!/);
|
|
|
|
// Core CLI commands
|
|
expect(content).toContain('pdftotext');
|
|
expect(content).toContain('pdfinfo');
|
|
expect(content).toContain('extract');
|
|
expect(content).toContain('fetch');
|
|
expect(content).toContain('info');
|
|
expect(content).toContain('list');
|
|
|
|
// Key options
|
|
expect(content).toContain('--layout');
|
|
expect(content).toContain('--pages');
|
|
});
|
|
|
|
it('container skill SKILL.md has correct frontmatter', () => {
|
|
const skillMdPath = path.join(skillDir, 'add', 'container', 'skills', 'pdf-reader', 'SKILL.md');
|
|
const content = fs.readFileSync(skillMdPath, 'utf-8');
|
|
|
|
expect(content).toContain('name: pdf-reader');
|
|
expect(content).toContain('allowed-tools: Bash(pdf-reader:*)');
|
|
expect(content).toContain('pdf-reader extract');
|
|
expect(content).toContain('pdf-reader fetch');
|
|
expect(content).toContain('pdf-reader info');
|
|
});
|
|
|
|
it('has all files declared in modifies', () => {
|
|
const dockerfile = path.join(skillDir, 'modify', 'container', 'Dockerfile');
|
|
const whatsappTs = path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.ts');
|
|
const whatsappTestTs = path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.test.ts');
|
|
|
|
expect(fs.existsSync(dockerfile)).toBe(true);
|
|
expect(fs.existsSync(whatsappTs)).toBe(true);
|
|
expect(fs.existsSync(whatsappTestTs)).toBe(true);
|
|
});
|
|
|
|
it('has intent files for all modified files', () => {
|
|
expect(
|
|
fs.existsSync(path.join(skillDir, 'modify', 'container', 'Dockerfile.intent.md')),
|
|
).toBe(true);
|
|
expect(
|
|
fs.existsSync(path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.ts.intent.md')),
|
|
).toBe(true);
|
|
expect(
|
|
fs.existsSync(
|
|
path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.test.ts.intent.md'),
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('modified Dockerfile includes poppler-utils and pdf-reader', () => {
|
|
const content = fs.readFileSync(
|
|
path.join(skillDir, 'modify', 'container', 'Dockerfile'),
|
|
'utf-8',
|
|
);
|
|
|
|
expect(content).toContain('poppler-utils');
|
|
expect(content).toContain('pdf-reader');
|
|
expect(content).toContain('/usr/local/bin/pdf-reader');
|
|
});
|
|
|
|
it('modified Dockerfile preserves core structure', () => {
|
|
const content = fs.readFileSync(
|
|
path.join(skillDir, 'modify', 'container', 'Dockerfile'),
|
|
'utf-8',
|
|
);
|
|
|
|
expect(content).toContain('FROM node:22-slim');
|
|
expect(content).toContain('chromium');
|
|
expect(content).toContain('agent-browser');
|
|
expect(content).toContain('WORKDIR /app');
|
|
expect(content).toContain('COPY agent-runner/');
|
|
expect(content).toContain('ENTRYPOINT');
|
|
expect(content).toContain('/workspace/group');
|
|
expect(content).toContain('USER node');
|
|
});
|
|
|
|
it('modified whatsapp.ts includes PDF attachment handling', () => {
|
|
const content = fs.readFileSync(
|
|
path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.ts'),
|
|
'utf-8',
|
|
);
|
|
|
|
expect(content).toContain('documentMessage');
|
|
expect(content).toContain('application/pdf');
|
|
expect(content).toContain('downloadMediaMessage');
|
|
expect(content).toContain('attachments');
|
|
expect(content).toContain('pdf-reader extract');
|
|
});
|
|
|
|
it('modified whatsapp.ts preserves core structure', () => {
|
|
const content = fs.readFileSync(
|
|
path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.ts'),
|
|
'utf-8',
|
|
);
|
|
|
|
// Core class and methods preserved
|
|
expect(content).toContain('class WhatsAppChannel');
|
|
expect(content).toContain('implements Channel');
|
|
expect(content).toContain('async connect()');
|
|
expect(content).toContain('async sendMessage(');
|
|
expect(content).toContain('isConnected()');
|
|
expect(content).toContain('ownsJid(');
|
|
expect(content).toContain('async disconnect()');
|
|
expect(content).toContain('async setTyping(');
|
|
|
|
// Core imports preserved
|
|
expect(content).toContain('ASSISTANT_NAME');
|
|
expect(content).toContain('STORE_DIR');
|
|
});
|
|
|
|
it('modified whatsapp.test.ts includes PDF attachment tests', () => {
|
|
const content = fs.readFileSync(
|
|
path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.test.ts'),
|
|
'utf-8',
|
|
);
|
|
|
|
expect(content).toContain('PDF');
|
|
expect(content).toContain('documentMessage');
|
|
expect(content).toContain('application/pdf');
|
|
});
|
|
|
|
it('modified whatsapp.test.ts preserves all existing test sections', () => {
|
|
const content = fs.readFileSync(
|
|
path.join(skillDir, 'modify', 'src', 'channels', 'whatsapp.test.ts'),
|
|
'utf-8',
|
|
);
|
|
|
|
// All existing test describe blocks preserved
|
|
expect(content).toContain("describe('connection lifecycle'");
|
|
expect(content).toContain("describe('authentication'");
|
|
expect(content).toContain("describe('reconnection'");
|
|
expect(content).toContain("describe('message handling'");
|
|
expect(content).toContain("describe('LID to JID translation'");
|
|
expect(content).toContain("describe('outgoing message queue'");
|
|
expect(content).toContain("describe('group metadata sync'");
|
|
expect(content).toContain("describe('ownsJid'");
|
|
expect(content).toContain("describe('setTyping'");
|
|
expect(content).toContain("describe('channel properties'");
|
|
});
|
|
});
|