Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
"workspaceFolder": "/workspace",
"postCreateCommand": "yarn install",
"postCreateCommand": "yarn install && mkdir -p /home/node/.m2 && cp /workspace/.devcontainer/maven-settings.xml /home/node/.m2/settings.xml",
"postStartCommand": "mkdir -p /home/node/.m2/repository",
"features": {
"ghcr.io/devcontainers/features/git:1": {
Expand Down
68 changes: 68 additions & 0 deletions .devcontainer/maven-settings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0
http://maven.apache.org/xsd/settings-1.2.0.xsd">

<!-- Override the default HTTP blocker to allow FOLIO repositories -->
<mirrors>
<mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*,!folio-nexus,!folio-snapshots,!apache.snapshots,!index-data-nexus</mirrorOf>
<name>Pseudo repository to mirror external repositories initially using HTTP.</name>
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>
</mirrors>

<profiles>
<!-- FOLIO Repository Profile -->
<profile>
<id>folio</id>
<repositories>
<repository>
<id>folio-nexus</id>
<name>FOLIO Maven Repository</name>
<url>https://repository.folio.org/repository/maven-folio/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>folio-snapshots</id>
<name>FOLIO Snapshot Repository</name>
<url>https://repository.folio.org/repository/snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>folio-nexus</id>
<name>FOLIO Maven Repository</name>
<url>https://repository.folio.org/repository/maven-folio/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>

<!-- Activate the FOLIO profile by default -->
<activeProfiles>
<activeProfile>folio</activeProfile>
</activeProfiles>

</settings>
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"start": "node dist/cli.js",
"dev": "ts-node src/cli.ts",
"test": "jest",
"test:unit": "jest --testPathIgnorePatterns='integration'",
"test:integration": "jest --testMatch='**/*.integration.test.ts' --runInBand",
"clean": "rm -rf dist"
},
"keywords": [
Expand Down
296 changes: 296 additions & 0 deletions src/__tests__/integration/cli.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import * as os from 'os';
import { ModuleEvaluator } from '../../module-evaluator';
import { ReportGenerator } from '../../utils/report-generator';
import { EvaluationConfig, ReportOptions } from '../../types';

describe('CLI Integration Tests', () => {
const testOutputDir = path.join(os.tmpdir(), 'folio-eval-integration-tests', Date.now().toString());

beforeAll(async () => {
// Create test output directory
await fs.ensureDir(testOutputDir);
});

afterAll(async () => {
// Clean up test output directory
try {
await fs.remove(testOutputDir);
} catch (error) {
console.warn('Failed to clean up test output directory:', error);
}
});

// Extended timeout for integration tests (5 minutes per test)
jest.setTimeout(300000);

describe('Repository Evaluation', () => {
test('should evaluate mod-search (Java backend module)', async () => {
const repoUrl = 'https://github.com/folio-org/mod-search';
const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: false,
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
const result = await evaluator.evaluateModule(repoUrl);

// Verify evaluation result structure
expect(result).toBeDefined();
expect(result.moduleName).toBe('mod-search');
expect(result.repositoryUrl).toBe(repoUrl);
expect(result.evaluatedAt).toBeDefined();
expect(result.language).toBe('Java');
expect(result.criteria).toBeInstanceOf(Array);
expect(result.criteria.length).toBeGreaterThan(0);

// Verify each result has required fields
result.criteria.forEach((criterionResult) => {
expect(criterionResult.criterionId).toBeDefined();
expect(criterionResult.status).toMatch(/pass|fail|manual|not_applicable/);
expect(criterionResult.evidence).toBeDefined();
});

// Generate reports
const reportGenerator = new ReportGenerator(testOutputDir);
const reportPaths = await reportGenerator.generateReports(result);

// Verify reports were generated
expect(reportPaths.htmlPath).toBeDefined();
expect(reportPaths.jsonPath).toBeDefined();

// Verify HTML report exists and has content
const htmlContent = await fs.readFile(reportPaths.htmlPath!, 'utf-8');
expect(htmlContent).toContain('mod-search');
expect(htmlContent).toContain('Evaluation Report');

// Verify JSON report is valid JSON
const jsonContent = await fs.readFile(reportPaths.jsonPath!, 'utf-8');
const jsonData = JSON.parse(jsonContent);
expect(jsonData.moduleName).toBe('mod-search');
expect(jsonData.criteria).toBeInstanceOf(Array);
});

test('should evaluate mod-source-record-storage (Java backend module)', async () => {
const repoUrl = 'https://github.com/folio-org/mod-source-record-storage';
const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: false,
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
const result = await evaluator.evaluateModule(repoUrl);

// Verify evaluation result structure
expect(result).toBeDefined();
expect(result.moduleName).toBe('mod-source-record-storage');
expect(result.repositoryUrl).toBe(repoUrl);
expect(result.evaluatedAt).toBeDefined();
expect(result.language).toBe('Java');
expect(result.criteria).toBeInstanceOf(Array);
expect(result.criteria.length).toBeGreaterThan(0);

// Generate reports
const reportGenerator = new ReportGenerator(testOutputDir);
const reportPaths = await reportGenerator.generateReports(result);

// Verify reports were generated
expect(reportPaths.htmlPath).toBeDefined();
expect(reportPaths.jsonPath).toBeDefined();

// Verify JSON structure
const jsonContent = await fs.readFile(reportPaths.jsonPath!, 'utf-8');
const jsonData = JSON.parse(jsonContent);
expect(jsonData.moduleName).toBe('mod-source-record-storage');
expect(jsonData.language).toBe('Java');
expect(jsonData.criteria).toBeInstanceOf(Array);
});

test('should evaluate ui-data-import (JavaScript/UI module)', async () => {
const repoUrl = 'https://github.com/folio-org/ui-data-import';
const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: false,
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
const result = await evaluator.evaluateModule(repoUrl);

// Verify evaluation result structure
expect(result).toBeDefined();
expect(result.moduleName).toBe('ui-data-import');
expect(result.repositoryUrl).toBe(repoUrl);
expect(result.evaluatedAt).toBeDefined();
expect(result.language).toBe('JavaScript');
expect(result.criteria).toBeInstanceOf(Array);
expect(result.criteria.length).toBeGreaterThan(0);

// Generate reports
const reportGenerator = new ReportGenerator(testOutputDir);
const reportPaths = await reportGenerator.generateReports(result);

// Verify reports were generated
expect(reportPaths.htmlPath).toBeDefined();
expect(reportPaths.jsonPath).toBeDefined();

// Verify JSON structure
const jsonContent = await fs.readFile(reportPaths.jsonPath!, 'utf-8');
const jsonData = JSON.parse(jsonContent);
expect(jsonData.moduleName).toBe('ui-data-import');
expect(jsonData.language).toBe('JavaScript');
expect(jsonData.criteria).toBeInstanceOf(Array);
});
});

describe('Report Generation Options', () => {
test('should generate only JSON report when outputHtml is false', async () => {
const repoUrl = 'https://github.com/folio-org/mod-search';
const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: false,
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
const result = await evaluator.evaluateModule(repoUrl);
expect(result).toBeDefined();

// Generate only JSON report
const reportGenerator = new ReportGenerator(testOutputDir);
const reportOptions: ReportOptions = {
outputHtml: false,
outputJson: true,
outputDir: testOutputDir
};
const reportPaths = await reportGenerator.generateReports(result, reportOptions);

// Verify only JSON report was generated
expect(reportPaths.htmlPath).toBeUndefined();
expect(reportPaths.jsonPath).toBeDefined();

// Verify JSON file exists
const jsonExists = await fs.pathExists(reportPaths.jsonPath!);
expect(jsonExists).toBe(true);
});

test('should generate only HTML report when outputJson is false', async () => {
const repoUrl = 'https://github.com/folio-org/mod-search';
const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: false,
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
const result = await evaluator.evaluateModule(repoUrl);
expect(result).toBeDefined();

// Generate only HTML report
const reportGenerator = new ReportGenerator(testOutputDir);
const reportOptions: ReportOptions = {
outputHtml: true,
outputJson: false,
outputDir: testOutputDir
};
const reportPaths = await reportGenerator.generateReports(result, reportOptions);

// Verify only HTML report was generated
expect(reportPaths.htmlPath).toBeDefined();
expect(reportPaths.jsonPath).toBeUndefined();

// Verify HTML file exists
const htmlExists = await fs.pathExists(reportPaths.htmlPath!);
expect(htmlExists).toBe(true);
});
});

describe('Cleanup Functionality', () => {
test('should clean up temporary clone directory by default', async () => {
const repoUrl = 'https://github.com/folio-org/mod-search';
const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: false,
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
await evaluator.evaluateModule(repoUrl);

// Check that no temporary directories remain in system temp
const tempDir = path.join(os.tmpdir(), 'folio-eval');
if (await fs.pathExists(tempDir)) {
const dirs = await fs.readdir(tempDir);
// There should be very few or no mod-search directories
const modSearchDirs = dirs.filter(d => d.startsWith('mod-search'));
expect(modSearchDirs.length).toBeLessThanOrEqual(1);
}
});

test('should not clean up when skipCleanup is true', async () => {
const repoUrl = 'https://github.com/folio-org/mod-search';
const tempDir = path.join(os.tmpdir(), 'folio-eval-test-nocleanup');

const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: true,
tempDir: tempDir,
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
await evaluator.evaluateModule(repoUrl);

// Check that temporary directory still exists
expect(await fs.pathExists(tempDir)).toBe(true);
const dirs = await fs.readdir(tempDir);
const modSearchDirs = dirs.filter(d => d.startsWith('mod-search'));
expect(modSearchDirs.length).toBeGreaterThan(0);

// Clean up manually
for (const dir of modSearchDirs) {
await fs.remove(path.join(tempDir, dir));
}
});
});

describe('Criteria Filtering', () => {
test('should filter results when criteria filter is provided', async () => {
const repoUrl = 'https://github.com/folio-org/mod-search';
const config: EvaluationConfig = {
outputDir: testOutputDir,
skipCleanup: false,
criteriaFilter: ['A001', 'S001'],
};

// Create evaluator with config
const evaluator = new ModuleEvaluator(config);

// Run evaluation
const result = await evaluator.evaluateModule(repoUrl);

// Verify only specified criteria are in results
expect(result.criteria.length).toBeLessThanOrEqual(2);
result.criteria.forEach((r) => {
expect(['A001', 'S001']).toContain(r.criterionId);
});
});
});
});
Loading