From c6dc89df36385e7c4effc647e767007e1311a69d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 23 Feb 2026 20:36:35 +0000
Subject: [PATCH 1/5] Initial plan
From 68858e947acf2f30c4cc6144ecb540a65d7b77c6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 23 Feb 2026 20:38:35 +0000
Subject: [PATCH 2/5] Add testing infrastructure with Traject smoke tests
Co-authored-by: alexdryden <47127862+alexdryden@users.noreply.github.com>
---
.github/workflows/test.yml | 39 +++++++++++++
.gitignore | 7 ++-
Gemfile | 8 +++
tests/README.md | 54 ++++++++++++++++++
tests/conftest.py | 20 +++++++
tests/unit/test_traject_smoke.py | 96 ++++++++++++++++++++++++++++++++
6 files changed, 223 insertions(+), 1 deletion(-)
create mode 100644 .github/workflows/test.yml
create mode 100644 Gemfile
create mode 100644 tests/README.md
create mode 100644 tests/conftest.py
create mode 100644 tests/unit/test_traject_smoke.py
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..27c956c
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,39 @@
+name: Tests
+
+on:
+ push:
+ branches: [ main, develop, copilot/** ]
+ pull_request:
+ branches: [ main, develop ]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.12'
+ cache: 'pip'
+
+ - name: Setup Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: '3.1'
+ bundler-cache: true
+
+ - name: Install Python dependencies
+ run: |
+ pip install -r requirements.txt
+ pip install pytest
+
+ - name: Install Ruby dependencies
+ run: |
+ bundle install
+
+ - name: Run tests
+ run: |
+ pytest tests/unit -v
diff --git a/.gitignore b/.gitignore
index 0d405d1..dd89a82 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,4 +60,9 @@ MANIFEST
.arcflow.yml
# PID files
-*.pid
\ No newline at end of file
+*.pid
+
+# Ruby/Bundler files
+Gemfile.lock
+.bundle/
+vendor/bundle/
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..ea35a2b
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,8 @@
+source 'https://rubygems.org'
+
+gem 'traject', '~> 3.0'
+
+# Optional: for testing
+group :test do
+ gem 'rspec', '~> 3.0'
+end
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..156d895
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,54 @@
+# ArcFlow Test Suite
+
+This directory contains tests for the ArcFlow project.
+
+## Test Structure
+
+- `unit/` - Fast unit tests for individual components
+- `conftest.py` - Shared test fixtures and configuration
+
+## Running Tests
+
+```bash
+# Run all tests
+pytest
+
+# Run only unit tests
+pytest tests/unit
+
+# Run with verbose output
+pytest -v
+
+# Run specific test file
+pytest tests/unit/test_traject_smoke.py
+```
+
+## Traject Smoke Tests
+
+Tests in `tests/unit/test_traject_smoke.py` verify traject configuration without requiring Solr.
+
+### What They Test
+- Ruby syntax validity of traject configs
+- Traject can load and parse configs
+- XML transformation logic (without indexing)
+
+### Setup Requirements
+- Ruby 3.1+
+- Bundler
+- Run `bundle install` to install traject gem
+
+### Performance
+- First run: ~60 seconds (includes gem install)
+- Cached runs: ~40 seconds (gems cached)
+- Still fast enough for CI/agent iteration
+
+### Skipping
+These tests skip gracefully if traject config doesn't exist yet.
+
+## Writing Tests
+
+When adding new tests:
+- Use pytest fixtures from `conftest.py`
+- Keep unit tests fast (< 1 second each)
+- Add integration tests to appropriate subdirectories
+- Use `pytest.skip()` for tests that require optional dependencies
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..f07a168
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,20 @@
+"""
+Test configuration and shared fixtures for arcflow test suite.
+"""
+import pytest
+
+
+@pytest.fixture
+def sample_eac_cpf_xml():
+ """Minimal valid EAC-CPF XML for testing"""
+ return '''
+
+
+ creator_people_1
+
+
+
+ Test Person
+
+
+'''
diff --git a/tests/unit/test_traject_smoke.py b/tests/unit/test_traject_smoke.py
new file mode 100644
index 0000000..fb8f476
--- /dev/null
+++ b/tests/unit/test_traject_smoke.py
@@ -0,0 +1,96 @@
+"""
+Traject smoke tests - verify traject config and XML processing work.
+
+These tests run traject without Solr to catch config errors quickly.
+Goal: < 60 seconds total including Ruby setup (with caching).
+"""
+
+import pytest
+import subprocess
+from pathlib import Path
+
+
+@pytest.fixture
+def sample_eac_cpf_xml():
+ """Minimal valid EAC-CPF XML for testing"""
+ return '''
+
+
+ creator_people_1
+ new
+
+ Test
+
+
+
+
+
+ Test Person
+
+
+
+'''
+
+
+def test_traject_config_syntax_valid():
+ """Verify traject config has valid Ruby syntax"""
+ # Find traject config (might be in different locations)
+ possible_paths = [
+ "traject_config_eac_cpf.rb",
+ "example_traject_config_eac_cpf.rb",
+ ]
+
+ config_path = None
+ for path in possible_paths:
+ if Path(path).exists():
+ config_path = path
+ break
+
+ if not config_path:
+ pytest.skip("No traject config found (expected if not yet created)")
+
+ # Ruby syntax check (fast, doesn't execute)
+ result = subprocess.run(
+ ["ruby", "-c", config_path],
+ capture_output=True,
+ text=True
+ )
+
+ assert result.returncode == 0, f"Invalid Ruby syntax: {result.stderr}"
+
+
+@pytest.mark.skipif(
+ not Path("example_traject_config_eac_cpf.rb").exists(),
+ reason="Traject config not yet available"
+)
+def test_traject_loads_config():
+ """Verify traject can load config without errors"""
+ result = subprocess.run(
+ ["bundle", "exec", "traject", "-c", "example_traject_config_eac_cpf.rb"],
+ capture_output=True,
+ text=True
+ )
+
+ # Should show usage/help, not crash
+ assert "error" not in result.stderr.lower() or result.returncode == 1
+
+
+@pytest.mark.skipif(
+ not Path("example_traject_config_eac_cpf.rb").exists(),
+ reason="Traject config not yet available"
+)
+def test_traject_processes_sample_xml(tmp_path, sample_eac_cpf_xml):
+ """Verify traject can transform XML without Solr (smoke test)"""
+ xml_file = tmp_path / "sample.xml"
+ xml_file.write_text(sample_eac_cpf_xml)
+
+ # Use NullWriter to process without Solr
+ result = subprocess.run([
+ "bundle", "exec", "traject",
+ "-c", "example_traject_config_eac_cpf.rb",
+ "-w", "Traject::NullWriter",
+ str(xml_file)
+ ], capture_output=True, text=True)
+
+ # Should complete without errors
+ assert result.returncode == 0, f"Traject processing failed: {result.stderr}"
From e66496957f2ae375fddc240a2924c8fb1c478c62 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 23 Feb 2026 20:40:47 +0000
Subject: [PATCH 3/5] Add traject_plus gem and fix smoke test writer class
Co-authored-by: alexdryden <47127862+alexdryden@users.noreply.github.com>
---
Gemfile | 1 +
tests/unit/test_traject_smoke.py | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Gemfile b/Gemfile
index ea35a2b..ed25ff3 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,7 @@
source 'https://rubygems.org'
gem 'traject', '~> 3.0'
+gem 'traject_plus'
# Optional: for testing
group :test do
diff --git a/tests/unit/test_traject_smoke.py b/tests/unit/test_traject_smoke.py
index fb8f476..0804518 100644
--- a/tests/unit/test_traject_smoke.py
+++ b/tests/unit/test_traject_smoke.py
@@ -84,11 +84,11 @@ def test_traject_processes_sample_xml(tmp_path, sample_eac_cpf_xml):
xml_file = tmp_path / "sample.xml"
xml_file.write_text(sample_eac_cpf_xml)
- # Use NullWriter to process without Solr
+ # Use DebugWriter to process without Solr
result = subprocess.run([
"bundle", "exec", "traject",
"-c", "example_traject_config_eac_cpf.rb",
- "-w", "Traject::NullWriter",
+ "-w", "Traject::DebugWriter",
str(xml_file)
], capture_output=True, text=True)
From 4bccc8c835a0bc43fba01b06a504d27a16f12ed1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 23 Feb 2026 20:41:52 +0000
Subject: [PATCH 4/5] Address code review feedback: fix fixture duplication and
test logic
Co-authored-by: alexdryden <47127862+alexdryden@users.noreply.github.com>
---
tests/README.md | 4 ++--
tests/conftest.py | 8 +++++++-
tests/unit/test_traject_smoke.py | 28 ++++------------------------
3 files changed, 13 insertions(+), 27 deletions(-)
diff --git a/tests/README.md b/tests/README.md
index 156d895..78c9ccc 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -39,8 +39,8 @@ Tests in `tests/unit/test_traject_smoke.py` verify traject configuration without
### Performance
- First run: ~60 seconds (includes gem install)
-- Cached runs: ~40 seconds (gems cached)
-- Still fast enough for CI/agent iteration
+- Cached runs: ~2 seconds (gems cached)
+- Fast enough for CI/agent iteration
### Skipping
These tests skip gracefully if traject config doesn't exist yet.
diff --git a/tests/conftest.py b/tests/conftest.py
index f07a168..0f86510 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -11,10 +11,16 @@ def sample_eac_cpf_xml():
creator_people_1
+ new
+
+ Test
+
- Test Person
+
+ Test Person
+
'''
diff --git a/tests/unit/test_traject_smoke.py b/tests/unit/test_traject_smoke.py
index 0804518..cc66633 100644
--- a/tests/unit/test_traject_smoke.py
+++ b/tests/unit/test_traject_smoke.py
@@ -10,28 +10,6 @@
from pathlib import Path
-@pytest.fixture
-def sample_eac_cpf_xml():
- """Minimal valid EAC-CPF XML for testing"""
- return '''
-
-
- creator_people_1
- new
-
- Test
-
-
-
-
-
- Test Person
-
-
-
-'''
-
-
def test_traject_config_syntax_valid():
"""Verify traject config has valid Ruby syntax"""
# Find traject config (might be in different locations)
@@ -71,8 +49,10 @@ def test_traject_loads_config():
text=True
)
- # Should show usage/help, not crash
- assert "error" not in result.stderr.lower() or result.returncode == 1
+ # Should show usage/help without crashing - exitcode 1 is expected for no input files
+ # But should not have load errors in stderr
+ if result.returncode != 1:
+ assert "error loading" not in result.stderr.lower(), f"Config load error: {result.stderr}"
@pytest.mark.skipif(
From 75b50592a07294f8c261ec5f7aa05119ec8e5c14 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 23 Feb 2026 20:43:43 +0000
Subject: [PATCH 5/5] Add explicit permissions to GitHub Actions workflow for
security
Co-authored-by: alexdryden <47127862+alexdryden@users.noreply.github.com>
---
.github/workflows/test.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 27c956c..c104ed5 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,6 +9,8 @@ on:
jobs:
test:
runs-on: ubuntu-latest
+ permissions:
+ contents: read
steps:
- uses: actions/checkout@v4