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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ yarn-error.log*
_build

src/version.ts
tests/fixtures/.tmp-deposit.xml

.yalc
yalc.lock
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

Utilities for creating crossref.org deposit metadata from node or the command line.

CLI e2e fixtures and golden XML: [`docs/e2e-fixtures.md`](./docs/e2e-fixtures.md).

To use from the command line, use the `-g` to create a global install.

```
Expand Down
65 changes: 65 additions & 0 deletions docs/e2e-fixtures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# CLI end-to-end fixtures

Regression fixtures for `crossref deposit` (and later `validate`). Kept **compact and owned** by this repo — shaped like SciPy proceedings papers, not wholesale copies of [scipy_proceedings/papers](https://github.com/scipy-conference/scipy_proceedings/tree/2025/papers).

## Goals

- Exercise real CLI → MyST load → deposit XML path
- Capture **golden XML from `main`** for regression against refactors (e.g. monorepo split)
- Stay small and maintainable (< ~100KB text)

## Layout

```
tests/fixtures/
shared/
proceedings.yml # SciPy-like venue / volume / editors (synthetic)
conference/
paper-a/ # ORCID, affiliations, pages, abstract, DOI cites
paper-b/ # equal_contributor, funding, subtitle
journal/
article/
preprint/
article/
dataset/
item/
golden/ # Normalized XML baselines
conference.xml
journal.xml
preprint.xml
dataset.xml
```

## Coverage matrix

| Fixture | `--type` | What it covers |
| -------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
| `conference/paper-a` + `paper-b` | `conference` | Multi-paper batch, venue/volume/editors, pages, abstract, ORCID, funding, equal contributors, DOI citations |
| `journal/article` | `journal` | Journal title/DOI, volume/issue |
| `preprint/article` | `preprint` | Standalone posted content |
| `dataset/item` | `dataset` | Database venue + dataset record |

## Design choices

- **From scratch** (not vendored SciPy trees): flatten `extends`, no images/templates, fake names/emails/ORCIDs.
- **DOIs always present** in frontmatter so deposits are non-interactive (no DOI checkbox prompts).
- **Stable batch id** via `--id` in tests; **normalize** `timestamp` (and any remaining UUIDs) before comparing to goldens.
- Depositor flags use CLI defaults or explicit `--name` / `--email` / `--registrant`.

## Running

```bash
npm test # unit + e2e (builds CLI for e2e)
npm run test:unit # unit only
npm run test:e2e # e2e only (builds CLI first)
npm run test:e2e:record # regenerates tests/fixtures/golden/*.xml
```

`test:e2e:record` should only be used intentionally (e.g. when deposit XML shape changes on purpose).

## Out of scope (v1)

- Inquirer / path-discovery flows
- DOI generation UI
- Submodule clone of full SciPy proceedings
- Schema validation e2e (separate; needs schema bundles)
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
"unlink": "npm uninstall -g crossref-utils;",
"link": "npm run unlink; npm link;",
"dev": "npm run copy:version && npm run link && esbuild src/cli/index.ts --bundle --outfile=dist/crossref.cjs --platform=node --external:fsevents --watch",
"test": "npm run copy:version && vitest run",
"test": "npm run copy:version && vitest run && npm run test:e2e",
"test:unit": "npm run copy:version && vitest run",
"test:e2e": "npm run build && npm run copy:version && vitest run --config vitest.e2e.config.ts",
"test:e2e:record": "npm run build && npm run copy:version && RECORD_GOLDEN=1 vitest run --config vitest.e2e.config.ts",
"test:watch": "npm run copy:version && vitest watch",
"lint": "eslint \"src/**/*.ts*\" -c ./.eslintrc.cjs",
"lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\"",
Expand Down
134 changes: 134 additions & 0 deletions tests/cli.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { describe, test, expect, beforeAll } from 'vitest';

const fixturesRoot = path.join(process.cwd(), 'tests/fixtures');
const goldenRoot = path.join(fixturesRoot, 'golden');
const cliBin = path.join(process.cwd(), 'dist/crossref.cjs');
const repoRoot = process.cwd();

const RECORD = process.env.RECORD_GOLDEN === '1';

/** Strip nondeterministic deposit head fields for stable comparisons. */
export function normalizeDepositXml(xml: string): string {
return xml
.replace(/<doi_batch_id>[^<]*<\/doi_batch_id>/g, '<doi_batch_id>FIXED_BATCH_ID</doi_batch_id>')
.replace(/<timestamp>[^<]*<\/timestamp>/g, '<timestamp>0</timestamp>')
.replace(/\r\n/g, '\n')
.trim();
}

function runDeposit(args: string[]): string {
const result = spawnSync(process.execPath, [cliBin, 'deposit', ...args], {
cwd: repoRoot,
encoding: 'utf8',
env: { ...process.env, NO_COLOR: '1' },
});
if (result.status !== 0) {
throw new Error(
`crossref deposit failed (${result.status}):\n${result.stdout}\n${result.stderr}`,
);
}
return result.stdout;
}

function depositToNormalizedXml(args: string[]): string {
const outFile = path.join(fixturesRoot, '.tmp-deposit.xml');
try {
runDeposit([...args, '--id', 'FIXED_BATCH_ID', '-o', outFile]);
const xml = fs.readFileSync(outFile, 'utf8');
return normalizeDepositXml(xml);
} finally {
if (fs.existsSync(outFile)) fs.unlinkSync(outFile);
}
}

function assertOrRecord(name: string, actual: string) {
const goldenPath = path.join(goldenRoot, name);
if (RECORD) {
fs.mkdirSync(goldenRoot, { recursive: true });
fs.writeFileSync(goldenPath, `${actual}\n`);
return;
}
if (!fs.existsSync(goldenPath)) {
throw new Error(`Missing golden ${goldenPath}. Run with RECORD_GOLDEN=1 to create baselines.`);
}
const expected = normalizeDepositXml(fs.readFileSync(goldenPath, 'utf8'));
expect(actual).toBe(expected);
}

describe('CLI e2e deposit fixtures', () => {
beforeAll(() => {
if (!fs.existsSync(cliBin)) {
throw new Error(`CLI not built: ${cliBin}. Run npm run build first.`);
}
});

test('conference multi-paper deposit', () => {
const paperA = path.join(fixturesRoot, 'conference/paper-a');
const paperB = path.join(fixturesRoot, 'conference/paper-b');
const actual = depositToNormalizedXml([
'--type',
'conference',
'--name',
'Fixture Depositor',
'--email',
'depositor@example.org',
paperA,
paperB,
]);
assertOrRecord('conference.xml', actual);
expect(actual).toContain('conference_paper');
expect(actual).toContain('10.99999/fixture.paper-a');
expect(actual).toContain('10.99999/fixture.paper-b');
}, 120000);

test('journal deposit', () => {
const article = path.join(fixturesRoot, 'journal/article');
const actual = depositToNormalizedXml([
'--type',
'journal',
'--name',
'Fixture Depositor',
'--email',
'depositor@example.org',
article,
]);
assertOrRecord('journal.xml', actual);
expect(actual).toContain('journal_article');
expect(actual).toContain('10.99999/fixture.journal-article');
}, 120000);

test('preprint deposit', () => {
const article = path.join(fixturesRoot, 'preprint/article');
const actual = depositToNormalizedXml([
'--type',
'preprint',
'--name',
'Fixture Depositor',
'--email',
'depositor@example.org',
article,
]);
assertOrRecord('preprint.xml', actual);
expect(actual).toContain('posted_content');
expect(actual).toContain('10.99999/fixture.preprint');
}, 120000);

test('dataset deposit', () => {
const item = path.join(fixturesRoot, 'dataset/item');
const actual = depositToNormalizedXml([
'--type',
'dataset',
'--name',
'Fixture Depositor',
'--email',
'depositor@example.org',
item,
]);
assertOrRecord('dataset.xml', actual);
expect(actual).toContain('dataset');
expect(actual).toContain('10.99999/fixture.dataset');
}, 120000);
});
10 changes: 10 additions & 0 deletions tests/fixtures/conference/paper-a/main.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: Compact fixtures for Crossref conference deposits
abstract: |
This synthetic abstract exercises deposit metadata extraction for conference papers.
It references a well-known paper with a DOI [](doi:10.25080/issn.2575-9752).
---

# Introduction

A short body paragraph with a citation to ensure DOI citations are collected [](doi:10.1038/nature14539).
31 changes: 31 additions & 0 deletions tests/fixtures/conference/paper-a/myst.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: 1
extends: ../../shared/proceedings.yml
project:
title: Compact fixtures for Crossref conference deposits
subtitle: A short synthetic article
description: |
Synthetic fixture abstract used for CLI end-to-end regression tests.
first_page: 1
last_page: 8
doi: 10.99999/fixture.paper-a
date: 2025-07-10
authors:
- name: Ada Author
email: ada@example.org
orcid: 0000-0001-2345-6789
corresponding: true
affiliations:
- Example University
- name: Blake Writer
email: blake@example.org
orcid: 0000-0002-3456-7890
affiliations:
- Example Lab
affiliations:
- id: Example University
name: Example University
ror: https://ror.org/05ggc9x63
- id: Example Lab
name: Example Lab
toc:
- file: main.md
9 changes: 9 additions & 0 deletions tests/fixtures/conference/paper-b/main.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: Second fixture paper with funding
abstract: |
Synthetic abstract for funding and equal-contributor coverage in conference deposits.
---

# Notes

Body text for paper B.
38 changes: 38 additions & 0 deletions tests/fixtures/conference/paper-b/myst.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
version: 1
extends: ../../shared/proceedings.yml
project:
title: Second fixture paper with funding
subtitle: Equal contributors and awards
description: Second synthetic article for multi-paper conference deposits.
first_page: 9
last_page: 12
doi: 10.99999/fixture.paper-b
date: 2025-07-10
authors:
- name: Casey Coauthor
email: casey@example.org
orcid: 0000-0003-4567-8901
equal_contributor: true
affiliations:
- Example Institute
- name: Dana Coauthor
email: dana@example.org
orcid: 0000-0004-5678-9012
equal_contributor: true
affiliations:
- Example Institute
affiliations:
- id: Example Institute
name: Example Institute
ror: https://ror.org/02mhbdp94
- id: Example Fund
name: Example Fund
institution: Example Funding Agency
funding:
- statement: Supported by Example Fund.
awards:
- id: EF-2025-001
sources:
- Example Fund
toc:
- file: main.md
9 changes: 9 additions & 0 deletions tests/fixtures/dataset/item/main.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: Compact dataset fixture
abstract: |
Synthetic dataset description used as deposit abstract/description.
---

# Data

Dataset fixture notes.
20 changes: 20 additions & 0 deletions tests/fixtures/dataset/item/myst.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: 1
project:
title: Compact dataset fixture
description: Synthetic dataset record for CLI e2e tests.
doi: 10.99999/fixture.dataset
date: 2025-02-01
zenodo: https://zenodo.org/records/9999999
venue:
title: Example Research Data Repository
doi: 10.99999/example-data
authors:
- name: Sam Scientist
email: sam@example.org
affiliations:
- Example Data Lab
affiliations:
- id: Example Data Lab
name: Example Data Lab
toc:
- file: main.md
1 change: 1 addition & 0 deletions tests/fixtures/golden/conference.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<doi_batch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5.3.1" xmlns="http://www.crossref.org/schema/5.3.1" xmlns:jats="http://www.ncbi.nlm.nih.gov/JATS1" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fr="http://www.crossref.org/fundref.xsd" xmlns:ai="http://www.crossref.org/AccessIndicators.xsd" xsi:schemaLocation="http://www.crossref.org/schema/5.3.1 http://www.crossref.org/schemas/crossref5.3.1.xsd"><head><doi_batch_id>FIXED_BATCH_ID</doi_batch_id><timestamp>0</timestamp><depositor><depositor_name>Fixture Depositor</depositor_name><email_address>depositor@example.org</email_address></depositor><registrant>Crossref</registrant></head><body><conference><contributors><person_name sequence="first" contributor_role="editor"><given_name>Alex</given_name><surname>Editor</surname><affiliations><institution><institution_name>Example University</institution_name><institution_id type="ror">https://ror.org/05ggc9x63</institution_id></institution></affiliations><alt-name><string-name>Alex Editor</string-name></alt-name></person_name><person_name sequence="additional" contributor_role="editor"><given_name>Blair</given_name><surname>Chair</surname><affiliations><institution><institution_name>Example Lab</institution_name></institution></affiliations><alt-name><string-name>Blair Chair</string-name></alt-name></person_name></contributors><event_metadata><conference_name>Example Science Conference, 2025</conference_name><conference_acronym>ExSci</conference_acronym><conference_number>1st</conference_number><conference_location>Example City</conference_location><conference_date>July 1 - July 5, 2025</conference_date></event_metadata><proceedings_series_metadata><series_metadata><titles><title>Proceedings of the Example Science Conference</title><original_language_title>Proceedings of the Example Science Conference</original_language_title></titles><issn>0000-0000</issn><doi_data><doi>10.99999/issn.0000-0000</doi><resource>https://doi.curvenote.com/10.99999/issn.0000-0000</resource></doi_data></series_metadata><proceedings_title>Proceedings of the 1st Example Science Conference</proceedings_title><proceedings_subject>Scientific Computing</proceedings_subject><publisher><publisher_name>Example Proceedings Press</publisher_name></publisher><publication_date media_type="online"><month>07</month><day>10</day><year>2025</year></publication_date><noisbn reason="simple_series"></noisbn><doi_data><doi>10.99999/proc.2025</doi><resource>https://doi.curvenote.com/10.99999/proc.2025</resource></doi_data></proceedings_series_metadata><conference_paper><contributors><person_name sequence="first" contributor_role="author"><given_name>Ada</given_name><surname>Author</surname><affiliations><institution><institution_name>Example University</institution_name><institution_id type="ror">https://ror.org/05ggc9x63</institution_id></institution></affiliations><ORCID>https://orcid.org/0000-0001-2345-6789</ORCID><alt-name><string-name>Ada Author</string-name></alt-name></person_name><person_name sequence="additional" contributor_role="author"><given_name>Blake</given_name><surname>Writer</surname><affiliations><institution><institution_name>Example Lab</institution_name></institution></affiliations><ORCID>https://orcid.org/0000-0002-3456-7890</ORCID><alt-name><string-name>Blake Writer</string-name></alt-name></person_name></contributors><titles><title>Compact fixtures for Crossref conference deposits</title><subtitle>A short synthetic article</subtitle></titles><jats:abstract><jats:p>Synthetic fixture abstract used for CLI end-to-end regression tests.</jats:p></jats:abstract><publication_date media_type="online"><month>07</month><day>10</day><year>2025</year></publication_date><pages><first_page>1</first_page><last_page>8</last_page></pages><ai:program name="AccessIndicators"><ai:free_to_read></ai:free_to_read><ai:license_ref applies_to="vor">https://creativecommons.org/licenses/by/4.0/</ai:license_ref></ai:program><doi_data><doi>10.99999/fixture.paper-a</doi><resource content_version="vor">https://doi.curvenote.com/10.99999/fixture.paper-a</resource></doi_data><citation_list><citation key="LeCun_2015"><doi>10.1038/nature14539</doi></citation></citation_list></conference_paper><conference_paper><contributors><person_name sequence="first" contributor_role="author"><given_name>Casey</given_name><surname>Coauthor</surname><affiliations><institution><institution_name>Example Institute</institution_name><institution_id type="ror">https://ror.org/02mhbdp94</institution_id></institution></affiliations><ORCID>https://orcid.org/0000-0003-4567-8901</ORCID><alt-name><string-name>Casey Coauthor</string-name></alt-name></person_name><person_name sequence="first" contributor_role="author"><given_name>Dana</given_name><surname>Coauthor</surname><affiliations><institution><institution_name>Example Institute</institution_name><institution_id type="ror">https://ror.org/02mhbdp94</institution_id></institution></affiliations><ORCID>https://orcid.org/0000-0004-5678-9012</ORCID><alt-name><string-name>Dana Coauthor</string-name></alt-name></person_name></contributors><titles><title>Second fixture paper with funding</title><subtitle>Equal contributors and awards</subtitle></titles><jats:abstract><jats:p>Second synthetic article for multi-paper conference deposits.</jats:p></jats:abstract><publication_date media_type="online"><month>07</month><day>10</day><year>2025</year></publication_date><pages><first_page>9</first_page><last_page>12</last_page></pages><ai:program name="AccessIndicators"><ai:free_to_read></ai:free_to_read><ai:license_ref applies_to="vor">https://creativecommons.org/licenses/by/4.0/</ai:license_ref></ai:program><doi_data><doi>10.99999/fixture.paper-b</doi><resource content_version="vor">https://doi.curvenote.com/10.99999/fixture.paper-b</resource></doi_data></conference_paper></conference></body></doi_batch>
1 change: 1 addition & 0 deletions tests/fixtures/golden/dataset.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<doi_batch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5.3.1" xmlns="http://www.crossref.org/schema/5.3.1" xmlns:jats="http://www.ncbi.nlm.nih.gov/JATS1" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fr="http://www.crossref.org/fundref.xsd" xmlns:ai="http://www.crossref.org/AccessIndicators.xsd" xsi:schemaLocation="http://www.crossref.org/schema/5.3.1 http://www.crossref.org/schemas/crossref5.3.1.xsd"><head><doi_batch_id>FIXED_BATCH_ID</doi_batch_id><timestamp>0</timestamp><depositor><depositor_name>Fixture Depositor</depositor_name><email_address>depositor@example.org</email_address></depositor><registrant>Crossref</registrant></head><body><database><database_metadata language="en"><titles><title>Example Research Data Repository</title></titles></database_metadata><dataset dataset_type="other"><contributors><person_name sequence="first" contributor_role="author"><given_name>Sam</given_name><surname>Scientist</surname><affiliations><institution><institution_name>Example Data Lab</institution_name></institution></affiliations><alt-name><string-name>Sam Scientist</string-name></alt-name></person_name></contributors><titles><title>Compact dataset fixture</title></titles><database_date><creation_date media_type="online"><month>02</month><day>01</day><year>2025</year></creation_date></database_date><description>Synthetic dataset record for CLI e2e tests.</description><program name="relations" xmlns="http://www.crossref.org/relations.xsd"><related_item><inter_work_relation relationship-type="isPartOf" identifier-type="doi">10.99999/example-data</inter_work_relation></related_item></program><doi_data><doi>10.99999/fixture.dataset</doi><resource content_version="vor">https://zenodo.org/records/9999999</resource></doi_data></dataset></database></body></doi_batch>
Loading
Loading