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
45 changes: 45 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Integration Tests

on:
pull_request:
branches: [main]
workflow_dispatch:

jobs:
integration:
runs-on: ubuntu-latest
env:
OVERLEAF_GIT_AUTHOR_NAME: OverleafMCP CI
OVERLEAF_GIT_AUTHOR_EMAIL: ci@overleafmcp.test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Write projects.json from secrets
run: |
cat > projects.json << 'EOF'
{
"projects": {
"example_project": {
"name": "Example Project",
"projectId": "${{ secrets.EXAMPLE_PROJECT_ID }}",
"gitToken": "${{ secrets.EXAMPLE_PROJECT_TOKEN }}",
"readOnly": false
},
"readonly_example_project": {
"name": "ReadOnly Example Project",
"projectId": "${{ secrets.READONLY_PROJECT_ID }}",
"gitToken": "${{ secrets.READONLY_PROJECT_TOKEN }}",
"readOnly": true
}
}
}
EOF
- name: Run integration tests
run: node --test test/integration.test.js
- name: Restore test files on failure
if: failure()
run: node test/restore.js
18 changes: 18 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Unit Tests

on:
pull_request:
branches: [dev, main]
workflow_dispatch:

jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: node --test test/unit.test.js
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules/
temp/
spec/
projects.json
.env
*.log
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)
![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
![MCP](https://img.shields.io/badge/MCP-compatible-purple)
![Unit Tests](https://github.com/SemPlaatsman/OverleafMCP/actions/workflows/unit-tests.yml/badge.svg)
![Integration Tests](https://github.com/SemPlaatsman/OverleafMCP/actions/workflows/integration-tests.yml/badge.svg)

> Give any MCP-compatible AI assistant direct access to your Overleaf projects.

Expand Down Expand Up @@ -284,6 +286,26 @@ Resolution order: `readOnly: true` takes precedence over everything and blocks a

---

## Testing

OverleafMCP has a two-layer test suite built on Node.js's built-in `node:test` module — no extra test dependencies required.

**Unit tests** cover pure logic: section and BibTeX parsing, path traversal protection, and anchor uniqueness. They run in under a second with no credentials or network access:

```bash
npm test
```

**Integration tests** run the full tool stack against a live Overleaf project via git, verifying every tool end-to-end including push verification via `listHistory`. They require a `projects.json` with writable and read-only test projects configured:

```bash
npm run test:integration
```

CI runs unit tests on every PR to `dev` and `main`. Integration tests run on PRs to `main` only.

---

## Attribution

See [ATTRIBUTION.md](./ATTRIBUTION.md) for credits to the open-source projects that informed this work.
Expand Down
4 changes: 3 additions & 1 deletion overleaf-git-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const SECTION_LEVELS = {
subparagraph: 6,
};

export const PREVIEW_MAX_LENGTH = 100;

export class OverleafGitClient {
constructor(projectId, gitToken, tempDir) {
this.projectId = projectId;
Expand Down Expand Up @@ -275,7 +277,7 @@ export class OverleafGitClient {
const contentStart = entry._cmdEndIndex;
const contentEnd = i + 1 < flat.length ? flat[i + 1].startIndex : content.length;
entry.content = content.substring(contentStart, contentEnd).trim();
entry.preview = entry.content.substring(0, 100).replace(/\s+/g, ' ');
entry.preview = entry.content.substring(0, PREVIEW_MAX_LENGTH).replace(/\s+/g, ' ');
delete entry._cmdEndIndex;
});

Expand Down
Loading
Loading